/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "TestCommon.h" #include "TestHarness.h" #include #include "mozilla/Atomics.h" #include "mozilla/Monitor.h" #include "nsINamedPipeService.h" #include "nsNetCID.h" #define PIPE_NAME "\\\\.\\pipe\\TestNPS" #define TEST_STR "Hello World" using namespace mozilla; class nsNamedPipeDataObserver : public nsINamedPipeDataObserver { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSINAMEDPIPEDATAOBSERVER explicit nsNamedPipeDataObserver(HANDLE aPipe); int Read(void* aBuffer, uint32_t aSize); int Write(const void* aBuffer, uint32_t aSize); uint32_t Transferred() const { return mBytesTransferred; } private: ~nsNamedPipeDataObserver() = default; HANDLE mPipe; OVERLAPPED mOverlapped; Atomic mBytesTransferred; Monitor mMonitor; }; NS_IMPL_ISUPPORTS(nsNamedPipeDataObserver, nsINamedPipeDataObserver) nsNamedPipeDataObserver::nsNamedPipeDataObserver(HANDLE aPipe) : mPipe(aPipe) , mOverlapped() , mBytesTransferred(0) , mMonitor("named-pipe") { mOverlapped.hEvent = CreateEventA(nullptr, TRUE, TRUE, "named-pipe"); } int nsNamedPipeDataObserver::Read(void* aBuffer, uint32_t aSize) { DWORD bytesRead = 0; if (!ReadFile(mPipe, aBuffer, aSize, &bytesRead, &mOverlapped)) { switch(GetLastError()) { case ERROR_IO_PENDING: { MonitorAutoLock lock(mMonitor); mMonitor.Wait(); } if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesRead, FALSE)) { fail("GetOverlappedResult failed"); return -1; } if (mBytesTransferred != bytesRead) { fail("GetOverlappedResult mismatch"); return -1; } break; default: fail("ReadFile error %d", GetLastError()); return -1; } } else { MonitorAutoLock lock(mMonitor); mMonitor.Wait(); if (mBytesTransferred != bytesRead) { fail("GetOverlappedResult mismatch"); return -1; } } mBytesTransferred = 0; passed("[read] match"); return bytesRead; } int nsNamedPipeDataObserver::Write(const void* aBuffer, uint32_t aSize) { DWORD bytesWritten = 0; if (!WriteFile(mPipe, aBuffer, aSize, &bytesWritten, &mOverlapped)) { switch(GetLastError()) { case ERROR_IO_PENDING: { MonitorAutoLock lock(mMonitor); mMonitor.Wait(); } if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesWritten, FALSE)) { fail("GetOverlappedResult failed"); return -1; } if (mBytesTransferred != bytesWritten) { fail("GetOverlappedResult mismatch"); return -1; } break; default: fail("WriteFile error %d", GetLastError()); return -1; } } else { MonitorAutoLock lock(mMonitor); mMonitor.Wait(); if (mBytesTransferred != bytesWritten) { fail("GetOverlappedResult mismatch"); return -1; } } mBytesTransferred = 0; passed("[write] match"); return bytesWritten; } NS_IMETHODIMP nsNamedPipeDataObserver::OnDataAvailable(uint32_t aBytesTransferred, void *aOverlapped) { if (aOverlapped != &mOverlapped) { fail("invalid overlapped object"); return NS_ERROR_FAILURE; } DWORD bytesTransferred = 0; BOOL ret = GetOverlappedResult(mPipe, reinterpret_cast(aOverlapped), &bytesTransferred, FALSE); if (!ret) { fail("GetOverlappedResult failed"); return NS_ERROR_FAILURE; } if (bytesTransferred != aBytesTransferred) { fail("GetOverlappedResult mismatch"); return NS_ERROR_FAILURE; } mBytesTransferred += aBytesTransferred; MonitorAutoLock lock(mMonitor); mMonitor.Notify(); return NS_OK; } NS_IMETHODIMP nsNamedPipeDataObserver::OnError(uint32_t aError, void *aOverlapped) { return NS_ERROR_NOT_IMPLEMENTED; } BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe); BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped); BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe) { if (!aPipe) { fail("Parameter aPipe is NULL\n"); return FALSE; } // FIXME: adjust parameters *aPipe = CreateNamedPipeA( PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 65536, 65536, 3000, NULL); if (*aPipe == INVALID_HANDLE_VALUE) { fail("CreateNamedPipe failed [%d]\n", GetLastError()); return FALSE; } return ConnectToNewClient(*aPipe, aOverlapped); } BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped) { if (ConnectNamedPipe(aPipe, aOverlapped)) { fail("Unexpected, overlapped ConnectNamedPipe() always returns 0.\n"); return FALSE; } switch (GetLastError()) { case ERROR_IO_PENDING: return TRUE; case ERROR_PIPE_CONNECTED: if (SetEvent(aOverlapped->hEvent)) break; default: // error fail("ConnectNamedPipe failed [%d]\n", GetLastError()); break; } return FALSE; } static nsresult CreateNamedPipe(LPHANDLE aServer, LPHANDLE aClient) { OVERLAPPED overlapped; overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); BOOL ret; ret = CreateAndConnectInstance(&overlapped, aServer); if (!ret) { fail("pipe server should be pending"); return NS_ERROR_FAILURE; } *aClient = CreateFileA(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr); if (*aClient == INVALID_HANDLE_VALUE) { fail("Unable to create pipe client"); CloseHandle(*aServer); return NS_ERROR_FAILURE; } DWORD pipeMode = PIPE_READMODE_MESSAGE; if (!SetNamedPipeHandleState(*aClient, &pipeMode, nullptr, nullptr)) { fail("SetNamedPipeHandleState error (%d)", GetLastError()); CloseHandle(*aServer); CloseHandle(*aClient); return NS_ERROR_FAILURE; } WaitForSingleObjectEx(overlapped.hEvent, INFINITE, TRUE); return NS_OK; } int main(int32_t argc, char* argv[]) { ScopedXPCOM xpcom("NamedPipeService"); if (xpcom.failed()) { fail("Unable to initalize XPCOM."); return -1; } nsresult rv; nsCOMPtr svc = do_GetService(NS_NAMEDPIPESERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) { fail("Unable to create named pipe service"); return -1; } HANDLE readPipe, writePipe; if (NS_FAILED(rv = CreateNamedPipe(&readPipe, &writePipe))) { fail("Unable to create pipes %d", GetLastError()); return -1; } RefPtr readObserver = new nsNamedPipeDataObserver(readPipe); RefPtr writeObserver = new nsNamedPipeDataObserver(writePipe); if (NS_WARN_IF(NS_FAILED(svc->AddDataObserver(readPipe, readObserver)))) { fail("Unable to add read data observer"); return -1; } if (NS_WARN_IF(NS_FAILED(svc->AddDataObserver(writePipe, writeObserver)))) { fail("Unable to add read data observer"); return -1; } if (writeObserver->Write(TEST_STR, sizeof(TEST_STR)) != sizeof(TEST_STR)) { fail("write error"); return -1; } char buffer[sizeof(TEST_STR)]; if (readObserver->Read(buffer, sizeof(buffer)) != sizeof(TEST_STR)) { fail("read error"); return -1; } if (strcmp(buffer, TEST_STR) != 0) { fail("I/O mismatch"); return -1; } if (NS_WARN_IF(NS_FAILED(svc->RemoveDataObserver(readPipe, readObserver)))) { fail("Unable to remove read data observer"); return -1; } if (NS_WARN_IF(NS_FAILED(svc->RemoveDataObserver(writePipe, writeObserver)))) { fail("Unable to remove read data observer"); return -1; } passed("Finish"); return 0; }