tor-browser

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

Http3WebTransportSession.cpp (17595B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 // HttpLog.h should generally be included first
      7 #include "HttpLog.h"
      8 #include "Http3WebTransportSession.h"
      9 #include "Http3WebTransportStream.h"
     10 #include "Http3Session.h"
     11 #include "Http3Stream.h"
     12 #include "nsHttpRequestHead.h"
     13 #include "nsHttpTransaction.h"
     14 #include "nsIClassOfService.h"
     15 #include "nsISocketTransport.h"
     16 #include "nsSocketTransportService2.h"
     17 #include "nsIOService.h"
     18 #include "nsHttpHandler.h"
     19 
     20 namespace mozilla::net {
     21 
     22 //-----------------------------------------------------------------------------
     23 // Http3TunnelStreamBase
     24 //-----------------------------------------------------------------------------
     25 
     26 Http3TunnelStreamBase::Http3TunnelStreamBase(nsAHttpTransaction* trans,
     27                                             Http3SessionBase* aHttp3Session)
     28    : Http3StreamBase(trans, aHttp3Session) {}
     29 
     30 bool Http3TunnelStreamBase::ConsumeHeaders(const char* buf, uint32_t avail,
     31                                           uint32_t* countUsed) {
     32  LOG3(("Http3TunnelStreamBase::ConsumeHeaders %p avail=%u.", this, avail));
     33 
     34  mFlatHttpRequestHeaders.Append(buf, avail);
     35  // We can use the simple double crlf because firefox is the
     36  // only client we are parsing
     37  int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
     38 
     39  if (endHeader == kNotFound) {
     40    // We don't have all the headers yet
     41    LOG3(
     42        ("Http3TunnelStreamBase::ConsumeHeaders %p "
     43         "Need more header bytes. Len = %zu",
     44         this, mFlatHttpRequestHeaders.Length()));
     45    *countUsed = avail;
     46    return false;
     47  }
     48 
     49  uint32_t oldLen = mFlatHttpRequestHeaders.Length();
     50  mFlatHttpRequestHeaders.SetLength(endHeader + 2);
     51  *countUsed = avail - (oldLen - endHeader) + 4;
     52 
     53  return true;
     54 }
     55 
     56 nsresult Http3TunnelStreamBase::TryActivating() {
     57  LOG(("Http3TunnelStreamBase::TryActivating [this=%p]", this));
     58  nsHttpRequestHead* head = mTransaction->RequestHead();
     59 
     60  nsAutoCString host;
     61  nsresult rv = head->GetHeader(nsHttp::Host, host);
     62  if (NS_FAILED(rv)) {
     63    MOZ_ASSERT(false);
     64    return rv;
     65  }
     66  nsAutoCString path;
     67  head->Path(path);
     68 
     69  return mSession->TryActivating(""_ns, ""_ns, host, path,
     70                                 mFlatHttpRequestHeaders, &mStreamId, this);
     71 }
     72 
     73 nsresult Http3TunnelStreamBase::ReadSegments() {
     74  LOG(("Http3TunnelStreamBase::ReadSegments %p mSendState=%d mRecvState=%d",
     75       this, mSendState, mRecvState));
     76  if (mSendState == PROCESSING_DATAGRAM) {
     77    return OnProcessDatagram();
     78  }
     79 
     80  if ((mRecvState == RECV_DONE) || (mRecvState == ACTIVE) ||
     81      (mRecvState == CLOSE_PENDING)) {
     82    // Don't transmit any request frames if the peer cannot respond or respone
     83    // is already done.
     84    LOG3(
     85        ("Http3TunnelStreamBase %p ReadSegments request stream aborted due to"
     86         " response side closure\n",
     87         this));
     88    return NS_ERROR_ABORT;
     89  }
     90 
     91  nsresult rv = NS_OK;
     92  uint32_t transactionBytes;
     93  bool again = true;
     94  do {
     95    transactionBytes = 0;
     96    rv = mSocketOutCondition = NS_OK;
     97    LOG(("Http3TunnelStreamBase::ReadSegments state=%d [this=%p]", mSendState,
     98         this));
     99    switch (mSendState) {
    100      case PREPARING_HEADERS: {
    101        rv = mTransaction->ReadSegmentsAgain(
    102            this, nsIOService::gDefaultSegmentSize, &transactionBytes, &again);
    103      } break;
    104      case WAITING_TO_ACTIVATE: {
    105        // A transaction that had already generated its headers before it was
    106        // queued at the session level (due to concurrency concerns) may not
    107        // call onReadSegment off the ReadSegments() stack above.
    108        LOG3(
    109            ("Http3TunnelStreamBase %p ReadSegments forcing OnReadSegment "
    110             "call\n",
    111             this));
    112        uint32_t wasted = 0;
    113        nsresult rv2 = OnReadSegment("", 0, &wasted);
    114        LOG3(("  OnReadSegment returned 0x%08" PRIx32,
    115              static_cast<uint32_t>(rv2)));
    116      } break;
    117      default:
    118        transactionBytes = 0;
    119        rv = NS_OK;
    120        break;
    121    }
    122 
    123    LOG(("Http3TunnelStreamBase::ReadSegments rv=0x%" PRIx32
    124         " read=%u sock-cond=%" PRIx32 " again=%d [this=%p]",
    125         static_cast<uint32_t>(rv), transactionBytes,
    126         static_cast<uint32_t>(mSocketOutCondition), again, this));
    127 
    128    // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
    129    if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) {
    130      rv = NS_OK;
    131      transactionBytes = 0;
    132    }
    133 
    134    if (NS_FAILED(rv)) {
    135      // if the transaction didn't want to write any more data, then
    136      // wait for the transaction to call ResumeSend.
    137      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    138        rv = NS_OK;
    139      }
    140      again = false;
    141    } else if (NS_FAILED(mSocketOutCondition)) {
    142      if (mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) {
    143        rv = mSocketOutCondition;
    144      }
    145      again = false;
    146    } else if (!transactionBytes) {
    147      mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_WAITING_FOR, 0);
    148 
    149      rv = NS_OK;
    150      again = false;
    151    }
    152    // write more to the socket until error or end-of-request...
    153  } while (again && gHttpHandler->Active());
    154  return rv;
    155 }
    156 
    157 nsresult Http3TunnelStreamBase::WriteSegments() {
    158  LOG(("Http3TunnelStreamBase::WriteSegments [this=%p]", this));
    159  nsresult rv = NS_OK;
    160  uint32_t countWrittenSingle = 0;
    161  bool again = true;
    162 
    163  if (mRecvState == CLOSE_PENDING) {
    164    OnClosePending();
    165    mRecvState = RECV_DONE;
    166    // This will closed the steam because the stream is Done().
    167    return NS_OK;
    168  }
    169 
    170  do {
    171    mSocketInCondition = NS_OK;
    172    countWrittenSingle = 0;
    173    rv = mTransaction->WriteSegmentsAgain(
    174        this, nsIOService::gDefaultSegmentSize, &countWrittenSingle, &again);
    175    LOG(("Http3TunnelStreamBase::WriteSegments rv=0x%" PRIx32
    176         " countWrittenSingle=%" PRIu32 " socketin=%" PRIx32 " [this=%p]",
    177         static_cast<uint32_t>(rv), countWrittenSingle,
    178         static_cast<uint32_t>(mSocketInCondition), this));
    179    if (mTransaction->IsDone()) {
    180      // An HTTP transaction used for setting up a  WebTransport session will
    181      // receive only response headers and afterward, it will be marked as
    182      // done. At this point, the session negotiation has finished and the
    183      // WebTransport session transfers into the ACTIVE state.
    184      mRecvState = ACTIVE;
    185    }
    186 
    187    if (NS_FAILED(rv)) {
    188      // if the transaction didn't want to take any more data, then
    189      // wait for the transaction to call ResumeRecv.
    190      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    191        rv = NS_OK;
    192      }
    193      again = false;
    194    } else if (NS_FAILED(mSocketInCondition)) {
    195      if (mSocketInCondition != NS_BASE_STREAM_WOULD_BLOCK) {
    196        rv = mSocketInCondition;
    197      }
    198      again = false;
    199    } else if (mRecvState == ACTIVE) {
    200      // again = false;
    201      again = OnActivated();
    202    }
    203    // read more from the socket until error...
    204  } while (again && gHttpHandler->Active());
    205 
    206  return rv;
    207 }
    208 
    209 void Http3TunnelStreamBase::SetResponseHeaders(
    210    nsTArray<uint8_t>& aResponseHeaders, bool fin, bool interim) {
    211  MOZ_ASSERT(mRecvState == BEFORE_HEADERS ||
    212             mRecvState == READING_INTERIM_HEADERS);
    213  mFlatResponseHeaders.AppendElements(aResponseHeaders);
    214  mRecvState = (interim) ? READING_INTERIM_HEADERS : READING_HEADERS;
    215 }
    216 
    217 nsresult Http3TunnelStreamBase::OnWriteSegment(char* buf, uint32_t count,
    218                                               uint32_t* countWritten) {
    219  LOG(("Http3TunnelStreamBase::OnWriteSegment [this=%p, state=%d", this,
    220       mRecvState));
    221  nsresult rv = NS_OK;
    222  switch (mRecvState) {
    223    case BEFORE_HEADERS: {
    224      *countWritten = 0;
    225      rv = NS_BASE_STREAM_WOULD_BLOCK;
    226    } break;
    227    case READING_HEADERS:
    228    case READING_INTERIM_HEADERS: {
    229      // SetResponseHeaders should have been previously called.
    230      MOZ_ASSERT(!mFlatResponseHeaders.IsEmpty(), "Headers empty!");
    231      *countWritten = (mFlatResponseHeaders.Length() > count)
    232                          ? count
    233                          : mFlatResponseHeaders.Length();
    234      memcpy(buf, mFlatResponseHeaders.Elements(), *countWritten);
    235 
    236      mFlatResponseHeaders.RemoveElementsAt(0, *countWritten);
    237      if (mFlatResponseHeaders.Length() == 0) {
    238        if (mRecvState == READING_INTERIM_HEADERS) {
    239          // neqo makes sure that fin cannot be received before the final
    240          // headers are received.
    241          mRecvState = BEFORE_HEADERS;
    242        } else {
    243          mRecvState = ACTIVE;
    244        }
    245      }
    246 
    247      if (*countWritten == 0) {
    248        rv = NS_BASE_STREAM_WOULD_BLOCK;
    249      } else {
    250        mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RECEIVING_FROM,
    251                                        0);
    252      }
    253    } break;
    254    case ACTIVE:
    255    case CLOSE_PENDING:
    256    case RECV_DONE:
    257      rv = NS_ERROR_UNEXPECTED;
    258  }
    259 
    260  // Remember the error received from lower layers. A stream pipe may overwrite
    261  // it.
    262  // If rv == NS_OK this will reset mSocketInCondition.
    263  mSocketInCondition = rv;
    264 
    265  return rv;
    266 }
    267 
    268 nsresult Http3TunnelStreamBase::OnReadSegment(const char* buf, uint32_t count,
    269                                              uint32_t* countRead) {
    270  LOG(("Http3TunnelStreamBase::OnReadSegment count=%u state=%d [this=%p]",
    271       count, mSendState, this));
    272 
    273  nsresult rv = NS_OK;
    274 
    275  switch (mSendState) {
    276    case PREPARING_HEADERS: {
    277      if (!ConsumeHeaders(buf, count, countRead)) {
    278        break;
    279      }
    280      mSendState = WAITING_TO_ACTIVATE;
    281    }
    282      [[fallthrough]];
    283    case WAITING_TO_ACTIVATE:
    284      rv = TryActivating();
    285      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    286        LOG3(
    287            ("Http3TunnelStreamBase::OnReadSegment %p cannot activate now. "
    288             "queued.\n",
    289             this));
    290        break;
    291      }
    292      if (NS_FAILED(rv)) {
    293        LOG3(
    294            ("Http3TunnelStreamBase::OnReadSegment %p cannot activate "
    295             "error=0x%" PRIx32 ".",
    296             this, static_cast<uint32_t>(rv)));
    297        break;
    298      }
    299 
    300      // Successfully activated.
    301      mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_SENDING_TO, 0);
    302 
    303      mSendState = PROCESSING_DATAGRAM;
    304      break;
    305    default:
    306      MOZ_ASSERT(false, "We are done sending this request!");
    307      rv = NS_ERROR_UNEXPECTED;
    308      break;
    309  }
    310 
    311  mSocketOutCondition = rv;
    312 
    313  return mSocketOutCondition;
    314 }
    315 
    316 void Http3TunnelStreamBase::TransactionIsDone(nsresult aResult) {
    317  mTransaction->Close(aResult);
    318  mTransaction = nullptr;
    319 }
    320 
    321 //-----------------------------------------------------------------------------
    322 // Http3WebTransportSession
    323 //-----------------------------------------------------------------------------
    324 
    325 Http3WebTransportSession::Http3WebTransportSession(nsAHttpTransaction* trans,
    326                                                   Http3Session* aHttp3Session)
    327    : Http3TunnelStreamBase(trans, aHttp3Session) {
    328  LOG(("Http3WebTransportSession ctor %p", this));
    329 }
    330 
    331 Http3WebTransportSession::~Http3WebTransportSession() = default;
    332 
    333 uint64_t Http3WebTransportSession::GetStreamId() const {
    334  return Http3StreamBase::StreamId();
    335 }
    336 
    337 void Http3WebTransportSession::Close(nsresult aResult) {
    338  LOG(("Http3WebTransportSession::Close %p", this));
    339  if (mListener) {
    340    mListener->OnSessionClosed(NS_SUCCEEDED(aResult), 0, ""_ns);
    341    mListener = nullptr;
    342  }
    343  if (mTransaction) {
    344    mTransaction->Close(aResult);
    345    mTransaction = nullptr;
    346  }
    347  mRecvState = RECV_DONE;
    348  mSendState = SEND_DONE;
    349 
    350  if (mSession) {
    351    mSession->CloseWebTransportConn();
    352    mSession = nullptr;
    353  }
    354 }
    355 
    356 void Http3WebTransportSession::OnClosePending() {
    357  mSession->CloseWebTransport(mStreamId, mStatus, mReason);
    358 }
    359 
    360 void Http3WebTransportSession::OnSessionClosed(bool aCleanly, uint32_t aStatus,
    361                                               const nsACString& aReason) {
    362  if (mTransaction) {
    363    mTransaction->Close(NS_BASE_STREAM_CLOSED);
    364    mTransaction = nullptr;
    365  }
    366  if (mListener) {
    367    mListener->OnSessionClosed(aCleanly, aStatus, aReason);
    368    mListener = nullptr;
    369  }
    370  mRecvState = RECV_DONE;
    371  mSendState = SEND_DONE;
    372 
    373  mSession->CloseWebTransportConn();
    374 }
    375 
    376 void Http3WebTransportSession::CloseSession(uint32_t aStatus,
    377                                            const nsACString& aReason) {
    378  if ((mRecvState != CLOSE_PENDING) && (mRecvState != RECV_DONE)) {
    379    mStatus = aStatus;
    380    mReason = aReason;
    381    mSession->ConnectSlowConsumer(this);
    382    mRecvState = CLOSE_PENDING;
    383    mSendState = SEND_DONE;
    384  }
    385  mListener = nullptr;
    386 }
    387 
    388 void Http3WebTransportSession::CreateOutgoingBidirectionalStream(
    389    std::function<void(Result<RefPtr<WebTransportStreamBase>, nsresult>&&)>&&
    390        aCallback) {
    391  return CreateStreamInternal(true, std::move(aCallback));
    392 }
    393 
    394 void Http3WebTransportSession::CreateOutgoingUnidirectionalStream(
    395    std::function<void(Result<RefPtr<WebTransportStreamBase>, nsresult>&&)>&&
    396        aCallback) {
    397  return CreateStreamInternal(false, std::move(aCallback));
    398 }
    399 
    400 void Http3WebTransportSession::CreateStreamInternal(
    401    bool aBidi,
    402    std::function<void(Result<RefPtr<WebTransportStreamBase>, nsresult>&&)>&&
    403        aCallback) {
    404  LOG(("Http3WebTransportSession::CreateStreamInternal this=%p aBidi=%d", this,
    405       aBidi));
    406  if (mRecvState != ACTIVE) {
    407    aCallback(Err(NS_ERROR_NOT_AVAILABLE));
    408    return;
    409  }
    410 
    411  RefPtr<Http3WebTransportStream> stream =
    412      aBidi ? new Http3WebTransportStream(mSession, mStreamId,
    413                                          WebTransportStreamType::BiDi,
    414                                          std::move(aCallback))
    415            : new Http3WebTransportStream(mSession, mStreamId,
    416                                          WebTransportStreamType::UniDi,
    417                                          std::move(aCallback));
    418  mSession->StreamHasDataToWrite(stream);
    419  // Put the newly created stream in to |mStreams| to keep it alive.
    420  mStreams.AppendElement(std::move(stream));
    421 }
    422 
    423 // This is called by Http3Session::TryActivatingWebTransportStream. When called,
    424 // this means a WebTransport stream is successfully activated and the stream
    425 // will be managed by Http3Session.
    426 void Http3WebTransportSession::RemoveWebTransportStream(
    427    Http3WebTransportStream* aStream) {
    428  LOG(
    429      ("Http3WebTransportSession::RemoveWebTransportStream "
    430       "this=%p aStream=%p",
    431       this, aStream));
    432  DebugOnly<bool> existed = mStreams.RemoveElement(aStream);
    433  MOZ_ASSERT(existed);
    434 }
    435 
    436 already_AddRefed<Http3WebTransportStream>
    437 Http3WebTransportSession::OnIncomingWebTransportStream(
    438    WebTransportStreamType aType, uint64_t aId) {
    439  LOG(
    440      ("Http3WebTransportSession::OnIncomingWebTransportStream "
    441       "this=%p",
    442       this));
    443 
    444  if (mRecvState != ACTIVE) {
    445    return nullptr;
    446  }
    447 
    448  MOZ_ASSERT(!mTransaction);
    449  RefPtr<Http3WebTransportStream> stream =
    450      new Http3WebTransportStream(mSession, mStreamId, aType, aId);
    451  if (NS_FAILED(stream->InitInputPipe())) {
    452    return nullptr;
    453  }
    454 
    455  if (aType == WebTransportStreamType::BiDi) {
    456    if (NS_FAILED(stream->InitOutputPipe())) {
    457      return nullptr;
    458    }
    459  }
    460 
    461  if (!mListener) {
    462    return nullptr;
    463  }
    464 
    465  if (nsCOMPtr<WebTransportSessionEventListenerInternal> listener =
    466          do_QueryInterface(mListener)) {
    467    listener->OnIncomingStreamAvailableInternal(stream);
    468  }
    469  return stream.forget();
    470 }
    471 
    472 void Http3WebTransportSession::SendDatagram(nsTArray<uint8_t>&& aData,
    473                                            uint64_t aTrackingId) {
    474  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    475  LOG(("Http3WebTransportSession::SendDatagram this=%p", this));
    476  if (mSendState != PROCESSING_DATAGRAM) {
    477    return;
    478  }
    479 
    480  mSession->SendDatagram(this, aData, aTrackingId);
    481  mSession->StreamHasDataToWrite(this);
    482 }
    483 
    484 void Http3WebTransportSession::OnDatagramReceived(nsTArray<uint8_t>&& aData) {
    485  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    486  LOG(("Http3WebTransportSession::OnDatagramReceived this=%p", this));
    487  if (mRecvState != ACTIVE || !mListener) {
    488    return;
    489  }
    490 
    491  if (nsCOMPtr<WebTransportSessionEventListenerInternal> listener =
    492          do_QueryInterface(mListener)) {
    493    listener->OnDatagramReceivedInternal(std::move(aData));
    494  }
    495 }
    496 
    497 void Http3WebTransportSession::GetMaxDatagramSize() {
    498  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    499  if (mRecvState != ACTIVE || !mListener) {
    500    return;
    501  }
    502 
    503  uint64_t size = mSession->MaxDatagramSize(mStreamId);
    504  mListener->OnMaxDatagramSize(size);
    505 }
    506 
    507 void Http3WebTransportSession::OnOutgoingDatagramOutCome(
    508    uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) {
    509  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    510  LOG(("Http3WebTransportSession::OnOutgoingDatagramOutCome this=%p id=%" PRIx64
    511       ", outCome=%d mRecvState=%d",
    512       this, aId, static_cast<uint32_t>(aOutCome), mRecvState));
    513  if (mRecvState != ACTIVE || !mListener || !aId) {
    514    return;
    515  }
    516 
    517  mListener->OnOutgoingDatagramOutCome(aId, aOutCome);
    518 }
    519 
    520 void Http3WebTransportSession::OnStreamStopSending(uint64_t aId,
    521                                                   nsresult aError) {
    522  LOG(("OnStreamStopSending id:%" PRId64, aId));
    523  if (!mListener) {
    524    return;
    525  }
    526 
    527  mListener->OnStopSending(aId, aError);
    528 }
    529 
    530 void Http3WebTransportSession::OnStreamReset(uint64_t aId, nsresult aError) {
    531  LOG(("OnStreamReset id:%" PRId64, aId));
    532  if (!mListener) {
    533    return;
    534  }
    535 
    536  mListener->OnResetReceived(aId, aError);
    537 }
    538 
    539 }  // namespace mozilla::net