tor-browser

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

TestNamedPipeService.cpp (7880B)


      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 
      9 #include <windows.h>
     10 
     11 #include "mozilla/Atomics.h"
     12 #include "mozilla/gtest/MozAssertions.h"
     13 #include "mozilla/Monitor.h"
     14 #include "nsNamedPipeService.h"
     15 #include "nsNetCID.h"
     16 
     17 #define PIPE_NAME L"\\\\.\\pipe\\TestNPS"
     18 #define TEST_STR "Hello World"
     19 
     20 using namespace mozilla;
     21 
     22 /**
     23 * Unlike a monitor, an event allows a thread to wait on another thread
     24 * completing an action without regard to ordering of the wait and the notify.
     25 */
     26 class Event {
     27 public:
     28  explicit Event(const char* aName) : mMonitor(aName) {}
     29 
     30  ~Event() = default;
     31 
     32  void Set() {
     33    MonitorAutoLock lock(mMonitor);
     34    MOZ_RELEASE_ASSERT(!mSignaled);
     35    mSignaled = true;
     36    mMonitor.Notify();
     37  }
     38  void Wait() {
     39    MonitorAutoLock lock(mMonitor);
     40    while (!mSignaled) {
     41      lock.Wait();
     42    }
     43    mSignaled = false;
     44  }
     45 
     46 private:
     47  Monitor mMonitor MOZ_UNANNOTATED;
     48  bool mSignaled = false;
     49 };
     50 
     51 class nsNamedPipeDataObserver final : public nsINamedPipeDataObserver {
     52 public:
     53  NS_DECL_THREADSAFE_ISUPPORTS
     54  NS_DECL_NSINAMEDPIPEDATAOBSERVER
     55 
     56  explicit nsNamedPipeDataObserver(HANDLE aPipe);
     57 
     58  int Read(void* aBuffer, uint32_t aSize);
     59  int Write(const void* aBuffer, uint32_t aSize);
     60 
     61  uint32_t Transferred() const { return mBytesTransferred; }
     62 
     63 private:
     64  ~nsNamedPipeDataObserver() = default;
     65 
     66  HANDLE mPipe;
     67  OVERLAPPED mOverlapped;
     68  Atomic<uint32_t> mBytesTransferred;
     69  Event mEvent;
     70 };
     71 
     72 NS_IMPL_ISUPPORTS(nsNamedPipeDataObserver, nsINamedPipeDataObserver)
     73 
     74 nsNamedPipeDataObserver::nsNamedPipeDataObserver(HANDLE aPipe)
     75    : mPipe(aPipe), mOverlapped(), mBytesTransferred(0), mEvent("named-pipe") {
     76  mOverlapped.hEvent = CreateEventA(nullptr, TRUE, TRUE, "named-pipe");
     77 }
     78 
     79 int nsNamedPipeDataObserver::Read(void* aBuffer, uint32_t aSize) {
     80  DWORD bytesRead = 0;
     81  if (!ReadFile(mPipe, aBuffer, aSize, &bytesRead, &mOverlapped)) {
     82    switch (GetLastError()) {
     83      case ERROR_IO_PENDING: {
     84        mEvent.Wait();
     85      }
     86        if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesRead, FALSE)) {
     87          ADD_FAILURE() << "GetOverlappedResult failed";
     88          return -1;
     89        }
     90        if (mBytesTransferred != bytesRead) {
     91          ADD_FAILURE() << "GetOverlappedResult mismatch";
     92          return -1;
     93        }
     94 
     95        break;
     96      default:
     97        ADD_FAILURE() << "ReadFile error " << GetLastError();
     98        return -1;
     99    }
    100  } else {
    101    mEvent.Wait();
    102 
    103    if (mBytesTransferred != bytesRead) {
    104      ADD_FAILURE() << "GetOverlappedResult mismatch";
    105      return -1;
    106    }
    107  }
    108 
    109  mBytesTransferred = 0;
    110  return bytesRead;
    111 }
    112 
    113 int nsNamedPipeDataObserver::Write(const void* aBuffer, uint32_t aSize) {
    114  DWORD bytesWritten = 0;
    115  if (!WriteFile(mPipe, aBuffer, aSize, &bytesWritten, &mOverlapped)) {
    116    switch (GetLastError()) {
    117      case ERROR_IO_PENDING: {
    118        mEvent.Wait();
    119      }
    120        if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesWritten, FALSE)) {
    121          ADD_FAILURE() << "GetOverlappedResult failed";
    122          return -1;
    123        }
    124        if (mBytesTransferred != bytesWritten) {
    125          ADD_FAILURE() << "GetOverlappedResult mismatch";
    126          return -1;
    127        }
    128 
    129        break;
    130      default:
    131        ADD_FAILURE() << "WriteFile error " << GetLastError();
    132        return -1;
    133    }
    134  } else {
    135    mEvent.Wait();
    136 
    137    if (mBytesTransferred != bytesWritten) {
    138      ADD_FAILURE() << "GetOverlappedResult mismatch";
    139      return -1;
    140    }
    141  }
    142 
    143  mBytesTransferred = 0;
    144  return bytesWritten;
    145 }
    146 
    147 NS_IMETHODIMP
    148 nsNamedPipeDataObserver::OnDataAvailable(uint32_t aBytesTransferred,
    149                                         void* aOverlapped) {
    150  if (aOverlapped != &mOverlapped) {
    151    ADD_FAILURE() << "invalid overlapped object";
    152    return NS_ERROR_FAILURE;
    153  }
    154 
    155  DWORD bytesTransferred = 0;
    156  BOOL ret =
    157      GetOverlappedResult(mPipe, reinterpret_cast<LPOVERLAPPED>(aOverlapped),
    158                          &bytesTransferred, FALSE);
    159 
    160  if (!ret) {
    161    ADD_FAILURE() << "GetOverlappedResult failed";
    162    return NS_ERROR_FAILURE;
    163  }
    164 
    165  if (bytesTransferred != aBytesTransferred) {
    166    ADD_FAILURE() << "GetOverlappedResult mismatch";
    167    return NS_ERROR_FAILURE;
    168  }
    169 
    170  mBytesTransferred += aBytesTransferred;
    171  mEvent.Set();
    172 
    173  return NS_OK;
    174 }
    175 
    176 NS_IMETHODIMP
    177 nsNamedPipeDataObserver::OnError(uint32_t aError, void* aOverlapped) {
    178  return NS_ERROR_NOT_IMPLEMENTED;
    179 }
    180 
    181 BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe);
    182 BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped);
    183 
    184 BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe) {
    185  // FIXME: adjust parameters
    186  *aPipe =
    187      CreateNamedPipeW(PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
    188                       PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1,
    189                       65536, 65536, 3000, NULL);
    190 
    191  if (*aPipe == INVALID_HANDLE_VALUE) {
    192    ADD_FAILURE() << "CreateNamedPipe failed " << GetLastError();
    193    return FALSE;
    194  }
    195 
    196  return ConnectToNewClient(*aPipe, aOverlapped);
    197 }
    198 
    199 BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped) {
    200  if (ConnectNamedPipe(aPipe, aOverlapped)) {
    201    ADD_FAILURE()
    202        << "Unexpected, overlapped ConnectNamedPipe() always returns 0.";
    203    return FALSE;
    204  }
    205 
    206  switch (GetLastError()) {
    207    case ERROR_IO_PENDING:
    208      return TRUE;
    209 
    210    case ERROR_PIPE_CONNECTED:
    211      if (SetEvent(aOverlapped->hEvent)) break;
    212 
    213      [[fallthrough]];
    214    default:  // error
    215      ADD_FAILURE() << "ConnectNamedPipe failed " << GetLastError();
    216      break;
    217  }
    218 
    219  return FALSE;
    220 }
    221 
    222 static nsresult CreateNamedPipe(LPHANDLE aServer, LPHANDLE aClient) {
    223  OVERLAPPED overlapped;
    224  overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    225  BOOL ret;
    226 
    227  ret = CreateAndConnectInstance(&overlapped, aServer);
    228  if (!ret) {
    229    ADD_FAILURE() << "pipe server should be pending";
    230    return NS_ERROR_FAILURE;
    231  }
    232 
    233  *aClient = CreateFileW(PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
    234                         FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
    235                         OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
    236 
    237  if (*aClient == INVALID_HANDLE_VALUE) {
    238    ADD_FAILURE() << "Unable to create pipe client";
    239    CloseHandle(*aServer);
    240    return NS_ERROR_FAILURE;
    241  }
    242 
    243  DWORD pipeMode = PIPE_READMODE_MESSAGE;
    244  if (!SetNamedPipeHandleState(*aClient, &pipeMode, nullptr, nullptr)) {
    245    ADD_FAILURE() << "SetNamedPipeHandleState error " << GetLastError();
    246    CloseHandle(*aServer);
    247    CloseHandle(*aClient);
    248    return NS_ERROR_FAILURE;
    249  }
    250 
    251  WaitForSingleObjectEx(overlapped.hEvent, INFINITE, TRUE);
    252 
    253  return NS_OK;
    254 }
    255 
    256 TEST(TestNamedPipeService, Test)
    257 {
    258  nsCOMPtr<nsINamedPipeService> svc = net::NamedPipeService::GetOrCreate();
    259 
    260  HANDLE readPipe, writePipe;
    261  nsresult rv = CreateNamedPipe(&readPipe, &writePipe);
    262  ASSERT_NS_SUCCEEDED(rv);
    263 
    264  RefPtr<nsNamedPipeDataObserver> readObserver =
    265      new nsNamedPipeDataObserver(readPipe);
    266  RefPtr<nsNamedPipeDataObserver> writeObserver =
    267      new nsNamedPipeDataObserver(writePipe);
    268 
    269  ASSERT_NS_SUCCEEDED(svc->AddDataObserver(readPipe, readObserver));
    270  ASSERT_NS_SUCCEEDED(svc->AddDataObserver(writePipe, writeObserver));
    271  ASSERT_EQ(std::size_t(writeObserver->Write(TEST_STR, sizeof(TEST_STR))),
    272            sizeof(TEST_STR));
    273 
    274  char buffer[sizeof(TEST_STR)];
    275  ASSERT_EQ(std::size_t(readObserver->Read(buffer, sizeof(buffer))),
    276            sizeof(TEST_STR));
    277  ASSERT_STREQ(buffer, TEST_STR) << "I/O mismatch";
    278 
    279  ASSERT_NS_SUCCEEDED(svc->RemoveDataObserver(readPipe, readObserver));
    280  ASSERT_NS_SUCCEEDED(svc->RemoveDataObserver(writePipe, writeObserver));
    281 }