tor-browser

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

TestHttp3ConnectUDPStream.cpp (13458B)


      1 /* -*- Mode: C++; tab-width: 2; 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 #include "TestCommon.h"
      7 #include "gtest/gtest.h"
      8 #include "Http3ConnectUDPStream.h"
      9 #include "Http3Session.h"
     10 #include "nsIUDPSocket.h"
     11 #include "nsIIOService.h"
     12 #include "nsIProtocolProxyService.h"
     13 #include "nsIProtocolHandler.h"
     14 #include "nsThreadUtils.h"
     15 #include "nsStringStream.h"
     16 #include "nsProxyInfo.h"
     17 #include "nsHttpConnectionInfo.h"
     18 #include "nsHttpRequestHead.h"
     19 #include "nsHttpHandler.h"
     20 #include "mozilla/Components.h"
     21 
     22 using namespace mozilla;
     23 using namespace mozilla::net;
     24 
     25 static const char* kProxyHost = "proxy.org";
     26 static const char* kHost = "example.com";
     27 static const int32_t kPort = 4433;
     28 static const char* kMasqueTemplate =
     29    "/.well-known/masque/udp/{target_host}/{target_port}/";
     30 static const char* kPathHeader = "/.well-known/masque/udp/example.com/4433/";
     31 
     32 class Http3SessionStub final : public Http3SessionBase {
     33 public:
     34  NS_INLINE_DECL_REFCOUNTING(Http3SessionStub, override)
     35 
     36  nsresult TryActivating(const nsACString& aMethod, const nsACString& aScheme,
     37                         const nsACString& aAuthorityHeader,
     38                         const nsACString& aPath, const nsACString& aHeaders,
     39                         uint64_t* aStreamId,
     40                         Http3StreamBase* aStream) override {
     41    mPathHeader = aPath;
     42    mAuthHeader = aAuthorityHeader;
     43    return NS_OK;
     44  }
     45 
     46  void CloseSendingSide(uint64_t aStreamId) override {}
     47 
     48  void SendHTTPDatagram(uint64_t aStreamId, nsTArray<uint8_t>& aData,
     49                        uint64_t aTrackingId) override {
     50    mOutputData.AppendElements(aData);
     51  }
     52 
     53  nsresult SendRequestBody(uint64_t aStreamId, const char* buf, uint32_t count,
     54                           uint32_t* countRead) override {
     55    return NS_OK;
     56  }
     57 
     58  nsresult ReadResponseData(uint64_t aStreamId, char* aBuf, uint32_t aCount,
     59                            uint32_t* aCountWritten, bool* aFin) override {
     60    *aCountWritten = 0;
     61    *aFin = false;
     62    return NS_OK;
     63  }
     64 
     65  nsresult SendPriorityUpdateFrame(uint64_t aStreamId, uint8_t aPriorityUrgency,
     66                                   bool aPriorityIncremental) override {
     67    return NS_OK;
     68  }
     69 
     70  void ConnectSlowConsumer(Http3StreamBase* stream) override {}
     71 
     72  void CloseWebTransportConn() override {}
     73 
     74  void StreamHasDataToWrite(Http3StreamBase* aStream) override {
     75    mReadyForWrite.AppendElement(aStream);
     76  }
     77 
     78  nsresult CloseWebTransport(uint64_t aSessionId, uint32_t aError,
     79                             const nsACString& aMessage) override {
     80    return NS_OK;
     81  }
     82 
     83  void SendDatagram(Http3WebTransportSession* aSession,
     84                    nsTArray<uint8_t>& aData, uint64_t aTrackingId) override {}
     85 
     86  uint64_t MaxDatagramSize(uint64_t aSessionId) override { return 0; }
     87 
     88  nsresult TryActivatingWebTransportStream(uint64_t* aStreamId,
     89                                           Http3StreamBase* aStream) override {
     90    *aStreamId = 0;
     91    return NS_OK;
     92  }
     93 
     94  void ResetWebTransportStream(Http3WebTransportStream* aStream,
     95                               uint64_t aErrorCode) override {}
     96 
     97  void StreamStopSending(Http3WebTransportStream* aStream,
     98                         uint8_t aErrorCode) override {}
     99 
    100  void SetSendOrder(Http3StreamBase* aStream,
    101                    Maybe<int64_t> aSendOrder) override {}
    102 
    103  void ProcessOutput() {
    104    for (const auto& stream : mReadyForWrite) {
    105      (void)stream->ReadSegments();
    106    }
    107    mReadyForWrite.Clear();
    108  }
    109 
    110  void FinishTunnelSetup(nsAHttpTransaction* aTransaction) override {
    111    mFinishTunnelSetupCalled = true;
    112  }
    113 
    114  bool FinishTunnelSetupCalled() const { return mFinishTunnelSetupCalled; }
    115 
    116  nsTArray<uint8_t> TakeOutputData() { return std::move(mOutputData); }
    117 
    118  const nsCString& PathHeader() { return mPathHeader; }
    119  const nsCString& AuthHeader() { return mAuthHeader; }
    120 
    121 private:
    122  ~Http3SessionStub() = default;
    123 
    124  nsTArray<RefPtr<Http3StreamBase>> mReadyForWrite;
    125  nsTArray<uint8_t> mOutputData;
    126  nsCString mPathHeader;
    127  nsCString mAuthHeader;
    128  bool mFinishTunnelSetupCalled = false;
    129 };
    130 
    131 class DummyHttpTransaction : public nsAHttpTransaction {
    132 public:
    133  NS_DECL_THREADSAFE_ISUPPORTS
    134 
    135  DummyHttpTransaction() {
    136    nsCString buffer;
    137    buffer.AssignLiteral("capsule-protocol = ?1\r\n\r\n");
    138    NS_NewCStringInputStream(getter_AddRefs(mRequestStream), buffer);
    139 
    140    nsCOMPtr<nsIProtocolProxyService> pps;
    141    pps = mozilla::components::ProtocolProxy::Service();
    142    if (pps) {
    143      nsCOMPtr<nsIProxyInfo> info;
    144      nsresult rv = pps->NewMASQUEProxyInfo(
    145          nsCString(kProxyHost), -1, nsCString(kMasqueTemplate), ""_ns, ""_ns,
    146          0, 0, nullptr, getter_AddRefs(info));
    147      if (NS_FAILED(rv)) {
    148        return;
    149      }
    150      mConnInfo = new nsHttpConnectionInfo(
    151          nsCString(kHost), kPort, ""_ns, ""_ns,
    152          static_cast<nsProxyInfo*>(info.get()), OriginAttributes());
    153    }
    154  }
    155 
    156  static nsresult ReadRequestSegment(nsIInputStream* stream, void* closure,
    157                                     const char* buf, uint32_t offset,
    158                                     uint32_t count, uint32_t* countRead) {
    159    DummyHttpTransaction* trans = (DummyHttpTransaction*)closure;
    160    return trans->mReader->OnReadSegment(buf, count, countRead);
    161  }
    162 
    163  void SetConnection(nsAHttpConnection*) override {}
    164  nsAHttpConnection* Connection() override { return nullptr; }
    165  void GetSecurityCallbacks(nsIInterfaceRequestor**) override {}
    166  void OnTransportStatus(nsITransport* transport, nsresult status,
    167                         int64_t progress) override {}
    168  bool IsDone() override { return mIsDone; }
    169  nsresult Status() override { return NS_OK; }
    170  uint32_t Caps() override { return 0; }
    171  [[nodiscard]] nsresult ReadSegments(nsAHttpSegmentReader* reader,
    172                                      uint32_t count,
    173                                      uint32_t* countRead) override {
    174    mReader = reader;
    175    (void)mRequestStream->ReadSegments(ReadRequestSegment, this, count,
    176                                       countRead);
    177    mReader = nullptr;
    178    return NS_OK;
    179  }
    180  [[nodiscard]] nsresult WriteSegments(nsAHttpSegmentWriter* writer,
    181                                       uint32_t count,
    182                                       uint32_t* countWritten) override {
    183    char buf[1024];
    184    (void)writer->OnWriteSegment(buf, 1024, countWritten);
    185    mIsDone = true;
    186    return NS_OK;
    187  }
    188  void Close(nsresult reason) override {}
    189  nsHttpConnectionInfo* ConnectionInfo() override { return mConnInfo.get(); }
    190  void SetProxyConnectFailed() override {}
    191  nsHttpRequestHead* RequestHead() override {
    192    if (mRequestHead) {
    193      return mRequestHead.get();
    194    }
    195 
    196    mRequestHead = MakeUnique<nsHttpRequestHead>();
    197 
    198    (void)mRequestHead->SetHeader(nsHttp::Host, "example.com"_ns);
    199    return mRequestHead.get();
    200  }
    201  uint32_t Http1xTransactionCount() override { return 0; }
    202  [[nodiscard]] nsresult TakeSubTransactions(
    203      nsTArray<RefPtr<nsAHttpTransaction>>& outTransactions) override {
    204    return NS_OK;
    205  }
    206 
    207 private:
    208  virtual ~DummyHttpTransaction() = default;
    209 
    210  nsAHttpSegmentReader* mReader{nullptr};
    211  nsCOMPtr<nsIInputStream> mRequestStream;
    212  UniquePtr<nsHttpRequestHead> mRequestHead;
    213  bool mIsDone = false;
    214  RefPtr<nsHttpConnectionInfo> mConnInfo;
    215 };
    216 
    217 NS_IMPL_ISUPPORTS(DummyHttpTransaction, nsISupportsWeakReference)
    218 
    219 class UDPListener final : public nsIUDPSocketSyncListener {
    220 public:
    221  NS_DECL_ISUPPORTS
    222 
    223  UDPListener() = default;
    224 
    225  NS_IMETHOD OnPacketReceived(nsIUDPSocket* aSocket) override {
    226    nsTArray<uint8_t> data;
    227    NetAddr addr{};
    228    (void)aSocket->RecvWithAddr(&addr, data);
    229    mReceivedData.AppendElements(data);
    230    return NS_OK;
    231  }
    232 
    233  NS_IMETHOD OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) override {
    234    mOnStopListeningCalled = true;
    235    return NS_OK;
    236  }
    237 
    238  nsTArray<uint8_t> TakeInputData() { return std::move(mReceivedData); }
    239 
    240  bool OnStopListeningCalled() const { return mOnStopListeningCalled; }
    241 
    242 private:
    243  ~UDPListener() = default;
    244 
    245  bool mOnStopListeningCalled = false;
    246  nsTArray<uint8_t> mReceivedData;
    247 };
    248 
    249 NS_IMPL_ISUPPORTS(UDPListener, nsIUDPSocketSyncListener)
    250 
    251 static void InitHttpHandler() {
    252  if (gHttpHandler) {
    253    return;
    254  }
    255 
    256  nsresult rv;
    257  nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
    258  if (NS_FAILED(rv)) {
    259    return;
    260  }
    261 
    262  nsCOMPtr<nsIProtocolHandler> handler;
    263  rv = ios->GetProtocolHandler("http", getter_AddRefs(handler));
    264  if (NS_FAILED(rv)) {
    265    return;
    266  }
    267 }
    268 
    269 static already_AddRefed<Http3ConnectUDPStream> CreateUDPStream(
    270    Http3SessionStub* aSession) {
    271  RefPtr<DummyHttpTransaction> trans = new DummyHttpTransaction();
    272  RefPtr<Http3ConnectUDPStream> stream =
    273      new Http3ConnectUDPStream(trans, aSession, NS_GetCurrentThread());
    274 
    275  NetAddr peerAddr;
    276  peerAddr.InitFromString("127.0.0.1"_ns);
    277  stream->SetPeerAddr(peerAddr);
    278 
    279  aSession->StreamHasDataToWrite(stream);
    280  aSession->ProcessOutput();
    281 
    282  // HTTP/3 200
    283  static constexpr uint8_t kResponse[] = {0x48, 0x54, 0x54, 0x50, 0x2F, 0x33,
    284                                          0x20, 0x32, 0x30, 0x30, 0x0A, 0x0A};
    285  static constexpr uint32_t kResponseLen = sizeof(kResponse) - 1;
    286  nsTArray<uint8_t> response;
    287  response.AppendElements(kResponse, kResponseLen);
    288 
    289  stream->SetResponseHeaders(response, false, false);
    290  (void)stream->WriteSegments();
    291 
    292  return stream.forget();
    293 }
    294 
    295 namespace ConnectUdp::testing {
    296 
    297 static void CreateTestData(uint32_t aNumBytes, nsTArray<uint8_t>& aDataOut) {
    298  static constexpr const char kSampleText[] =
    299      "{\"type\":\"message\",\"id\":42,\"payload\":\"The quick brown fox jumps "
    300      "over the lazy dog.\"}";
    301  static constexpr uint32_t kSampleTextLen = sizeof(kSampleText) - 1;
    302 
    303  aDataOut.SetCapacity(aNumBytes);
    304 
    305  while (aNumBytes > 0) {
    306    uint32_t chunkSize = std::min(kSampleTextLen, aNumBytes);
    307    aDataOut.AppendElements(reinterpret_cast<const uint8_t*>(kSampleText),
    308                            chunkSize);
    309    aNumBytes -= chunkSize;
    310  }
    311 }
    312 
    313 static void ValidateData(nsTArray<uint8_t>& aInput,
    314                         nsTArray<uint8_t>& aExpectedData) {
    315  ASSERT_EQ(aExpectedData.Length(), aInput.Length());
    316  for (size_t i = 0; i < aExpectedData.Length(); i++) {
    317    ASSERT_EQ(aExpectedData[i], aInput[i]);
    318  }
    319 }
    320 
    321 }  // namespace ConnectUdp::testing
    322 
    323 TEST(ConnectUDP, SendDataBeforeActivate)
    324 {
    325  InitHttpHandler();
    326 
    327  RefPtr<Http3SessionStub> session = new Http3SessionStub();
    328  RefPtr<Http3ConnectUDPStream> stream =
    329      new Http3ConnectUDPStream(nullptr, session, NS_GetCurrentThread());
    330  nsCOMPtr<nsIUDPSocket> udp = static_cast<nsIUDPSocket*>(stream.get());
    331  ASSERT_TRUE(udp);
    332 
    333  NetAddr addr;
    334  addr.InitFromString("127.0.0.1"_ns);
    335  nsTArray<uint8_t> data;
    336  ConnectUdp::testing::CreateTestData(100, data);
    337  uint32_t written = 0;
    338  nsresult rv =
    339      udp->SendWithAddress(&addr, data.Elements(), data.Length(), &written);
    340  ASSERT_EQ(rv, NS_ERROR_NOT_AVAILABLE);
    341 }
    342 
    343 TEST(ConnectUDP, SendData)
    344 {
    345  InitHttpHandler();
    346 
    347  RefPtr<Http3SessionStub> session = new Http3SessionStub();
    348  RefPtr<Http3ConnectUDPStream> stream = CreateUDPStream(session);
    349 
    350  ASSERT_TRUE(session->FinishTunnelSetupCalled());
    351  ASSERT_TRUE(session->AuthHeader().EqualsASCII(kProxyHost));
    352  ASSERT_TRUE(session->PathHeader().EqualsASCII(kPathHeader));
    353 
    354  nsCOMPtr<nsIUDPSocket> udp = static_cast<nsIUDPSocket*>(stream.get());
    355  ASSERT_TRUE(udp);
    356 
    357  NetAddr peerAddr;
    358  peerAddr.InitFromString("127.0.0.1"_ns);
    359  nsTArray<uint8_t> data;
    360  ConnectUdp::testing::CreateTestData(100, data);
    361  uint32_t written = 0;
    362  nsresult rv =
    363      udp->SendWithAddress(&peerAddr, data.Elements(), data.Length(), &written);
    364  ASSERT_EQ(rv, NS_OK);
    365 
    366  NS_ProcessPendingEvents(nullptr);
    367 
    368  session->ProcessOutput();
    369 
    370  nsTArray<uint8_t> output = session->TakeOutputData();
    371  ConnectUdp::testing::ValidateData(data, output);
    372 
    373  data.Clear();
    374  ConnectUdp::testing::CreateTestData(200, data);
    375 
    376  rv =
    377      udp->SendWithAddress(&peerAddr, data.Elements(), data.Length(), &written);
    378  ASSERT_EQ(rv, NS_OK);
    379 
    380  NS_ProcessPendingEvents(nullptr);
    381 
    382  session->ProcessOutput();
    383  output = session->TakeOutputData();
    384  ConnectUdp::testing::ValidateData(data, output);
    385  ASSERT_EQ(stream->ByteCountSent(), 300u);
    386 
    387  udp->Close();
    388 }
    389 
    390 TEST(ConnectUDP, RecvData)
    391 {
    392  InitHttpHandler();
    393 
    394  RefPtr<Http3SessionStub> session = new Http3SessionStub();
    395  RefPtr<Http3ConnectUDPStream> stream = CreateUDPStream(session);
    396 
    397  ASSERT_TRUE(session->FinishTunnelSetupCalled());
    398  ASSERT_TRUE(session->AuthHeader().EqualsASCII(kProxyHost));
    399  ASSERT_TRUE(session->PathHeader().EqualsASCII(kPathHeader));
    400 
    401  nsCOMPtr<nsIUDPSocket> udp = static_cast<nsIUDPSocket*>(stream.get());
    402  ASSERT_TRUE(udp);
    403 
    404  RefPtr<UDPListener> listener = new UDPListener();
    405  udp->SyncListen(listener);
    406 
    407  nsTArray<uint8_t> data;
    408  ConnectUdp::testing::CreateTestData(100, data);
    409  stream->OnDatagramReceived(std::move(data));
    410 
    411  nsTArray<uint8_t> input = listener->TakeInputData();
    412  ASSERT_EQ(input.Length(), 100u);
    413 
    414  ConnectUdp::testing::CreateTestData(200, data);
    415  stream->OnDatagramReceived(std::move(data));
    416 
    417  input = listener->TakeInputData();
    418  ASSERT_EQ(input.Length(), 200u);
    419 
    420  ASSERT_EQ(stream->ByteCountReceived(), 300u);
    421 
    422  udp->Close();
    423 
    424  ASSERT_EQ(listener->OnStopListeningCalled(), true);
    425 }