tor-browser

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

proxy_tunnel_socket_unittest.cpp (8201B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // Original authors: ekr@rtfm.com; ryan@tokbox.com
      8 
      9 #include <numeric>
     10 
     11 #include "WebrtcTCPSocketWrapper.h"
     12 #include "nr_socket_tcp.h"
     13 
     14 #define GTEST_HAS_RTTI 0
     15 #include "gtest/gtest.h"
     16 #include "gtest_utils.h"
     17 
     18 using namespace mozilla;
     19 
     20 // update TestReadMultipleSizes if you change this
     21 MOZ_RUNINIT const std::string kHelloMessage =
     22    "HELLO IS IT ME YOU'RE LOOKING FOR?";
     23 
     24 class NrTcpSocketTest : public MtransportTest {
     25 public:
     26  NrTcpSocketTest()
     27      : mSProxy(nullptr),
     28        nr_socket_(nullptr),
     29        mEmptyArray(0),
     30        mReadChunkSize(0),
     31        mReadChunkSizeIncrement(1),
     32        mReadAllowance(-1),
     33        mConnected(false) {}
     34 
     35  void SetUp() override {
     36    MtransportTest::SetUp();
     37 
     38    mSProxy = new NrTcpSocket(nullptr);
     39    int r = nr_socket_create_int((void*)mSProxy.get(), mSProxy->vtbl(),
     40                                 &nr_socket_);
     41    ASSERT_EQ(0, r);
     42 
     43    // fake calling AsyncOpen() due to IPC calls. must be non-null
     44    mSProxy->AssignChannel_DoNotUse(new WebrtcTCPSocketWrapper(nullptr));
     45  }
     46 
     47  void TearDown() override {
     48    mSProxy->close();
     49    MtransportTest::TearDown();
     50  }
     51 
     52  static void readable_cb(NR_SOCKET s, int how, void* cb_arg) {
     53    NrTcpSocketTest* test = (NrTcpSocketTest*)cb_arg;
     54    size_t capacity = std::min(test->mReadChunkSize, test->mReadAllowance);
     55    nsTArray<uint8_t> array(capacity);
     56    size_t read;
     57 
     58    nr_socket_read(test->nr_socket_, (char*)array.Elements(), array.Capacity(),
     59                   &read, 0);
     60 
     61    ASSERT_TRUE(read <= array.Capacity());
     62    ASSERT_TRUE(test->mReadAllowance >= read);
     63 
     64    array.SetLength(read);
     65    test->mData.AppendElements(array);
     66    test->mReadAllowance -= read;
     67 
     68    // We may read more bytes each time we're called. This way we can ensure we
     69    // consume buffers partially and across multiple buffers.
     70    test->mReadChunkSize += test->mReadChunkSizeIncrement;
     71 
     72    if (test->mReadAllowance > 0) {
     73      NR_ASYNC_WAIT(s, how, &NrTcpSocketTest::readable_cb, cb_arg);
     74    }
     75  }
     76 
     77  static void writable_cb(NR_SOCKET s, int how, void* cb_arg) {
     78    NrTcpSocketTest* test = (NrTcpSocketTest*)cb_arg;
     79    test->mConnected = true;
     80  }
     81 
     82  const std::string DataString() {
     83    return std::string((char*)mData.Elements(), mData.Length());
     84  }
     85 
     86 protected:
     87  RefPtr<NrTcpSocket> mSProxy;
     88  nr_socket* nr_socket_;
     89 
     90  nsTArray<uint8_t> mData;
     91  nsTArray<uint8_t> mEmptyArray;
     92 
     93  uint32_t mReadChunkSize;
     94  uint32_t mReadChunkSizeIncrement;
     95  uint32_t mReadAllowance;
     96 
     97  bool mConnected;
     98 };
     99 
    100 TEST_F(NrTcpSocketTest, TestCreate) {}
    101 
    102 TEST_F(NrTcpSocketTest, TestConnected) {
    103  ASSERT_TRUE(!mConnected);
    104 
    105  NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_WRITE, &NrTcpSocketTest::writable_cb,
    106                this);
    107 
    108  // still not connected just registered for writes...
    109  ASSERT_TRUE(!mConnected);
    110 
    111  mSProxy->OnConnected("http"_ns);
    112 
    113  ASSERT_TRUE(mConnected);
    114 }
    115 
    116 TEST_F(NrTcpSocketTest, TestRead) {
    117  nsTArray<uint8_t> array;
    118  array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
    119 
    120  NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
    121                this);
    122  // this will read 0 bytes here
    123  mSProxy->OnRead(std::move(array));
    124 
    125  ASSERT_EQ(kHelloMessage.length(), mSProxy->CountUnreadBytes());
    126 
    127  // callback is still set but terminated due to 0 byte read
    128  // start callbacks again (first read is 0 then 1,2,3,...)
    129  mSProxy->OnRead(std::move(mEmptyArray));
    130 
    131  ASSERT_EQ(kHelloMessage.length(), mData.Length());
    132  ASSERT_EQ(kHelloMessage, DataString());
    133 }
    134 
    135 TEST_F(NrTcpSocketTest, TestReadConstantConsumeSize) {
    136  std::string data;
    137 
    138  // triangle number
    139  const int kCount = 32;
    140 
    141  //  ~17kb
    142  // triangle number formula n*(n+1)/2
    143  for (int i = 0; i < kCount * (kCount + 1) / 2; ++i) {
    144    data += kHelloMessage;
    145  }
    146 
    147  // decreasing buffer sizes
    148  for (int i = 0, start = 0; i < kCount; ++i) {
    149    int length = (kCount - i) * kHelloMessage.length();
    150 
    151    nsTArray<uint8_t> array;
    152    array.AppendElements(data.c_str() + start, length);
    153    start += length;
    154 
    155    mSProxy->OnRead(std::move(array));
    156  }
    157 
    158  ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes());
    159 
    160  // read same amount each callback
    161  mReadChunkSize = 128;
    162  mReadChunkSizeIncrement = 0;
    163  NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
    164                this);
    165 
    166  ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes());
    167 
    168  // start callbacks
    169  mSProxy->OnRead(std::move(mEmptyArray));
    170 
    171  ASSERT_EQ(data.length(), mData.Length());
    172  ASSERT_EQ(data, DataString());
    173 }
    174 
    175 TEST_F(NrTcpSocketTest, TestReadNone) {
    176  char buf[4096];
    177  size_t read = 0;
    178  int r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
    179 
    180  ASSERT_EQ(R_WOULDBLOCK, r);
    181 
    182  nsTArray<uint8_t> array;
    183  array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
    184  mSProxy->OnRead(std::move(array));
    185 
    186  ASSERT_EQ(kHelloMessage.length(), mSProxy->CountUnreadBytes());
    187 
    188  r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
    189 
    190  ASSERT_EQ(0, r);
    191  ASSERT_EQ(kHelloMessage.length(), read);
    192  ASSERT_EQ(kHelloMessage, std::string(buf, read));
    193 }
    194 
    195 TEST_F(NrTcpSocketTest, TestReadMultipleSizes) {
    196  using namespace std;
    197 
    198  string data;
    199  // 515 * kHelloMessage.length() == 17510
    200  const size_t kCount = 515;
    201  // randomly generated numbers, sums to 17510, 20 numbers
    202  vector<int> varyingSizes = {404,  622, 1463, 1597, 1676, 389, 389,
    203                              1272, 781, 81,   1030, 1450, 256, 812,
    204                              1571, 29,  1045, 911,  643,  1089};
    205 
    206  // changing varyingSizes or the test message breaks this so check here
    207  ASSERT_EQ(kCount, 17510 / kHelloMessage.length());
    208  ASSERT_EQ(17510, accumulate(varyingSizes.begin(), varyingSizes.end(), 0));
    209 
    210  // ~17kb
    211  for (size_t i = 0; i < kCount; ++i) {
    212    data += kHelloMessage;
    213  }
    214 
    215  nsTArray<uint8_t> array;
    216  array.AppendElements(data.c_str(), data.length());
    217 
    218  for (int amountToRead : varyingSizes) {
    219    nsTArray<uint8_t> buffer;
    220    buffer.AppendElements(array.Elements(), amountToRead);
    221    array.RemoveElementsAt(0, amountToRead);
    222    mSProxy->OnRead(std::move(buffer));
    223  }
    224 
    225  ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes());
    226 
    227  // don't need to read 0 on the first read, so start at 1 and keep going
    228  mReadChunkSize = 1;
    229  NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
    230                this);
    231  // start callbacks
    232  mSProxy->OnRead(std::move(mEmptyArray));
    233 
    234  ASSERT_EQ(data.length(), mData.Length());
    235  ASSERT_EQ(data, DataString());
    236 }
    237 
    238 TEST_F(NrTcpSocketTest, TestReadConsumeReadDrain) {
    239  std::string data;
    240  // ~26kb total; should be even
    241  const int kCount = 512;
    242 
    243  // there's some division by 2 here so check that kCount is even
    244  ASSERT_EQ(0, kCount % 2);
    245 
    246  for (int i = 0; i < kCount; ++i) {
    247    data += kHelloMessage;
    248    nsTArray<uint8_t> array;
    249    array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
    250    mSProxy->OnRead(std::move(array));
    251  }
    252 
    253  // read half at first
    254  mReadAllowance = kCount / 2 * kHelloMessage.length();
    255  // start by reading 1 byte
    256  mReadChunkSize = 1;
    257  NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
    258                this);
    259  mSProxy->OnRead(std::move(mEmptyArray));
    260 
    261  ASSERT_EQ(data.length() / 2, mSProxy->CountUnreadBytes());
    262  ASSERT_EQ(data.length() / 2, mData.Length());
    263 
    264  // fill read buffer back up
    265  for (int i = 0; i < kCount / 2; ++i) {
    266    data += kHelloMessage;
    267    nsTArray<uint8_t> array;
    268    array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
    269    mSProxy->OnRead(std::move(array));
    270  }
    271 
    272  // remove read limit
    273  mReadAllowance = -1;
    274  // used entire read allowance so we need to setup a new await
    275  NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
    276                this);
    277  // start callbacks
    278  mSProxy->OnRead(std::move(mEmptyArray));
    279 
    280  ASSERT_EQ(data.length(), mData.Length());
    281  ASSERT_EQ(data, DataString());
    282 }