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 }