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 }