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/. */
6 #include "nsNamedPipeIOLayer.h"
11 #include "mozilla/Atomics.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/Logging.h"
14 #include "mozilla/RefPtr.h"
15 #include "mozilla/Unused.h"
16 #include "mozilla/net/DNS.h"
17 #include "nsISupportsImpl.h"
18 #include "nsNamedPipeService.h"
19 #include "nsNativeCharsetUtils.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsSocketTransportService2.h"
24 #include "nsThreadUtils.h"
26 #include "private/pprio.h"
31 static mozilla::LazyLogModule
gNamedPipeLog("NamedPipeWin");
32 #define LOG_NPIO_DEBUG(...) \
33 MOZ_LOG(gNamedPipeLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
34 #define LOG_NPIO_ERROR(...) \
35 MOZ_LOG(gNamedPipeLog, mozilla::LogLevel::Error, (__VA_ARGS__))
37 PRDescIdentity nsNamedPipeLayerIdentity
;
38 static PRIOMethods nsNamedPipeLayerMethods
;
40 class NamedPipeInfo final
: public nsINamedPipeDataObserver
{
42 NS_DECL_THREADSAFE_ISUPPORTS
43 NS_DECL_NSINAMEDPIPEDATAOBSERVER
45 explicit NamedPipeInfo();
47 nsresult
Connect(const nsAString
& aPath
);
48 nsresult
Disconnect();
51 * Both blocking/non-blocking mode are supported in this class.
52 * The default mode is non-blocking mode, however, the client may change its
53 * mode to blocking mode during hand-shaking (e.g. nsSOCKSSocketInfo).
55 * In non-blocking mode, |Read| and |Write| should be called by clients only
56 * when |GetPollFlags| reports data availability. That is, the client calls
57 * |GetPollFlags| with |PR_POLL_READ| and/or |PR_POLL_WRITE| set, and
58 * according to the flags that set, |GetPollFlags| will check buffers status
59 * and decide corresponding actions:
61 * -------------------------------------------------------------------
62 * | | data in buffer | empty buffer |
63 * |---------------+-------------------------+-----------------------|
64 * | PR_POLL_READ | out: PR_POLL_READ | DoRead/DoReadContinue |
65 * |---------------+-------------------------+-----------------------|
66 * | PR_POLL_WRITE | DoWrite/DoWriteContinue | out: PR_POLL_WRITE |
67 * ------------------------------------------+------------------------
69 * |DoRead| and |DoWrite| initiate read/write operations asynchronously, and
70 * the |DoReadContinue| and |DoWriteContinue| are used to check the amount
71 * of the data are read/written to/from buffers.
73 * The output parameter and the return value of |GetPollFlags| are identical
74 * because we don't rely on the low-level select function to wait for data
75 * availability, we instead use nsNamedPipeService to poll I/O completeness.
77 * When client get |PR_POLL_READ| or |PR_POLL_WRITE| from |GetPollFlags|,
78 * they are able to use |Read| or |Write| to access the data in the buffer,
79 * and this is supposed to be very fast because no network traffic is
82 * In blocking mode, the flow is quite similar to non-blocking mode, but
83 * |DoReadContinue| and |DoWriteContinue| are never been used since the
84 * operations are done synchronously, which could lead to slow responses.
86 int32_t Read(void* aBuffer
, int32_t aSize
);
87 int32_t Write(const void* aBuffer
, int32_t aSize
);
89 // Like Read, but doesn't remove data in internal buffer.
90 uint32_t Peek(void* aBuffer
, int32_t aSize
);
92 // Number of bytes available to read in internal buffer.
93 int32_t Available() const;
97 // @return whether the buffer has been flushed
98 bool Sync(uint32_t aTimeout
);
99 void SetNonblocking(bool nonblocking
);
101 bool IsConnected() const;
102 bool IsNonblocking() const;
103 HANDLE
GetHandle() const;
105 // Initiate and check current status for read/write operations.
106 int16_t GetPollFlags(int16_t aInFlags
, int16_t* aOutFlags
);
109 virtual ~NamedPipeInfo();
112 * DoRead/DoWrite starts a read/write call synchronously or asynchronously
113 * depending on |mNonblocking|. In blocking mode, they return when the action
114 * has been done and in non-blocking mode it returns the number of bytes that
115 * were read/written if the operation is done immediately. If it takes some
116 * time to finish the operation, zero is returned and
117 * DoReadContinue/DoWriteContinue must be called to get async I/O result.
120 int32_t DoReadContinue();
122 int32_t DoWriteContinue();
125 * There was a write size limitation of named pipe,
126 * see https://support.microsoft.com/en-us/kb/119218 for more information.
127 * The limitation no longer exists, so feel free to change the value.
129 static const uint32_t kBufferSize
= 65536;
131 nsCOMPtr
<nsINamedPipeService
> mNamedPipeService
;
133 HANDLE mPipe
; // the handle to the named pipe.
134 OVERLAPPED mReadOverlapped
; // used for asynchronous read operations.
135 OVERLAPPED mWriteOverlapped
; // used for asynchronous write operations.
137 uint8_t mReadBuffer
[kBufferSize
]; // octets read from pipe.
140 * These indicates the [begin, end) position of the data in the buffer.
145 bool mHasPendingRead
; // previous asynchronous read is not finished yet.
147 uint8_t mWriteBuffer
[kBufferSize
]; // octets to be written to pipe.
150 * These indicates the [begin, end) position of the data in the buffer.
152 DWORD mWriteBegin
; // how many bytes are already written.
153 DWORD mWriteEnd
; // valid amount of data in the buffer.
155 bool mHasPendingWrite
; // previous asynchronous write is not finished yet.
158 * current blocking mode is non-blocking or not, accessed only in socket
163 Atomic
<DWORD
> mErrorCode
; // error code from Named Pipe Service.
166 NS_IMPL_ISUPPORTS(NamedPipeInfo
, nsINamedPipeDataObserver
)
168 NamedPipeInfo::NamedPipeInfo()
169 : mNamedPipeService(NamedPipeService::GetOrCreate()),
170 mPipe(INVALID_HANDLE_VALUE
),
173 mHasPendingRead(false),
176 mHasPendingWrite(false),
179 MOZ_ASSERT(mNamedPipeService
);
181 ZeroMemory(&mReadOverlapped
, sizeof(OVERLAPPED
));
182 ZeroMemory(&mWriteOverlapped
, sizeof(OVERLAPPED
));
185 NamedPipeInfo::~NamedPipeInfo() { MOZ_ASSERT(!mPipe
); }
187 // nsINamedPipeDataObserver
190 NamedPipeInfo::OnDataAvailable(uint32_t aBytesTransferred
, void* aOverlapped
) {
191 DebugOnly
<bool> isOnPipeServiceThread
;
192 MOZ_ASSERT(NS_SUCCEEDED(mNamedPipeService
->IsOnCurrentThread(
193 &isOnPipeServiceThread
)) &&
194 isOnPipeServiceThread
);
196 if (aOverlapped
== &mReadOverlapped
) {
197 LOG_NPIO_DEBUG("[%s] %p read %d bytes", __func__
, this, aBytesTransferred
);
198 } else if (aOverlapped
== &mWriteOverlapped
) {
199 LOG_NPIO_DEBUG("[%s] %p write %d bytes", __func__
, this, aBytesTransferred
);
201 MOZ_ASSERT(false, "invalid callback");
202 mErrorCode
= ERROR_INVALID_DATA
;
203 return NS_ERROR_FAILURE
;
206 mErrorCode
= ERROR_SUCCESS
;
208 // dispatch an empty event to trigger STS thread
209 gSocketTransportService
->Dispatch(
210 NS_NewRunnableFunction("NamedPipeInfo::OnDataAvailable", [] {}),
217 NamedPipeInfo::OnError(uint32_t aError
, void* aOverlapped
) {
218 DebugOnly
<bool> isOnPipeServiceThread
;
219 MOZ_ASSERT(NS_SUCCEEDED(mNamedPipeService
->IsOnCurrentThread(
220 &isOnPipeServiceThread
)) &&
221 isOnPipeServiceThread
);
223 LOG_NPIO_ERROR("[%s] error code=%d", __func__
, aError
);
226 // dispatch an empty event to trigger STS thread
227 gSocketTransportService
->Dispatch(
228 NS_NewRunnableFunction("NamedPipeInfo::OnError", [] {}),
234 // Named pipe operations
236 nsresult
NamedPipeInfo::Connect(const nsAString
& aPath
) {
237 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
240 CreateFileW(PromiseFlatString(aPath
).get(), GENERIC_READ
| GENERIC_WRITE
,
241 FILE_SHARE_READ
| FILE_SHARE_WRITE
, nullptr, OPEN_EXISTING
,
242 FILE_FLAG_OVERLAPPED
, nullptr);
244 if (pipe
== INVALID_HANDLE_VALUE
) {
245 LOG_NPIO_ERROR("[%p] CreateFile error (%lu)", this, GetLastError());
246 return NS_ERROR_FAILURE
;
249 DWORD pipeMode
= PIPE_READMODE_MESSAGE
;
250 if (!SetNamedPipeHandleState(pipe
, &pipeMode
, nullptr, nullptr)) {
251 LOG_NPIO_ERROR("[%p] SetNamedPipeHandleState error (%lu)", this,
254 return NS_ERROR_FAILURE
;
257 nsresult rv
= mNamedPipeService
->AddDataObserver(pipe
, this);
258 if (NS_WARN_IF(NS_FAILED(rv
))) {
263 HANDLE readEvent
= CreateEventA(nullptr, TRUE
, TRUE
, "NamedPipeRead");
264 if (NS_WARN_IF(!readEvent
|| readEvent
== INVALID_HANDLE_VALUE
)) {
266 return NS_ERROR_FAILURE
;
269 HANDLE writeEvent
= CreateEventA(nullptr, TRUE
, TRUE
, "NamedPipeWrite");
270 if (NS_WARN_IF(!writeEvent
|| writeEvent
== INVALID_HANDLE_VALUE
)) {
272 CloseHandle(readEvent
);
273 return NS_ERROR_FAILURE
;
277 mReadOverlapped
.hEvent
= readEvent
;
278 mWriteOverlapped
.hEvent
= writeEvent
;
282 nsresult
NamedPipeInfo::Disconnect() {
283 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
285 nsresult rv
= mNamedPipeService
->RemoveDataObserver(mPipe
, this);
286 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
290 if (mReadOverlapped
.hEvent
&&
291 mReadOverlapped
.hEvent
!= INVALID_HANDLE_VALUE
) {
292 CloseHandle(mReadOverlapped
.hEvent
);
293 mReadOverlapped
.hEvent
= nullptr;
296 if (mWriteOverlapped
.hEvent
&&
297 mWriteOverlapped
.hEvent
!= INVALID_HANDLE_VALUE
) {
298 CloseHandle(mWriteOverlapped
.hEvent
);
299 mWriteOverlapped
.hEvent
= nullptr;
305 int32_t NamedPipeInfo::Read(void* aBuffer
, int32_t aSize
) {
306 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
308 int32_t bytesRead
= Peek(aBuffer
, aSize
);
311 mReadBegin
+= bytesRead
;
317 int32_t NamedPipeInfo::Write(const void* aBuffer
, int32_t aSize
) {
318 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
319 MOZ_ASSERT(mWriteBegin
<= mWriteEnd
);
321 if (!IsConnected()) {
323 PR_SetError(PR_NOT_CONNECTED_ERROR
, 0);
327 if (mWriteBegin
== mWriteEnd
) {
328 mWriteBegin
= mWriteEnd
= 0;
331 int32_t bytesToWrite
=
332 std::min
<int32_t>(aSize
, sizeof(mWriteBuffer
) - mWriteEnd
);
333 MOZ_ASSERT(bytesToWrite
>= 0);
335 if (bytesToWrite
== 0) {
336 PR_SetError(IsNonblocking() ? PR_WOULD_BLOCK_ERROR
: PR_IO_PENDING_ERROR
,
341 memcpy(&mWriteBuffer
[mWriteEnd
], aBuffer
, bytesToWrite
);
342 mWriteEnd
+= bytesToWrite
;
345 * Triggers internal write operation by calling |GetPollFlags|.
346 * This is required for callers that use blocking I/O because they don't call
347 * |GetPollFlags| to write data, but this also works for non-blocking I/O.
350 GetPollFlags(PR_POLL_WRITE
, &outFlag
);
355 uint32_t NamedPipeInfo::Peek(void* aBuffer
, int32_t aSize
) {
356 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
357 MOZ_ASSERT(mReadBegin
<= mReadEnd
);
359 if (!IsConnected()) {
361 PR_SetError(PR_NOT_CONNECTED_ERROR
, 0);
366 * If there's nothing in the read buffer, try to trigger internal read
367 * operation by calling |GetPollFlags|. This is required for callers that
368 * use blocking I/O because they don't call |GetPollFlags| to read data,
369 * but this also works for non-blocking I/O.
373 GetPollFlags(PR_POLL_READ
, &outFlag
);
375 if (!(outFlag
& PR_POLL_READ
)) {
376 PR_SetError(IsNonblocking() ? PR_WOULD_BLOCK_ERROR
: PR_IO_PENDING_ERROR
,
382 // Available() can't return more than what fits to the buffer at the read
384 int32_t bytesRead
= std::min
<int32_t>(aSize
, Available());
385 MOZ_ASSERT(bytesRead
>= 0);
386 MOZ_ASSERT(mReadBegin
+ bytesRead
<= mReadEnd
);
387 memcpy(aBuffer
, &mReadBuffer
[mReadBegin
], bytesRead
);
391 int32_t NamedPipeInfo::Available() const {
392 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
393 MOZ_ASSERT(mReadBegin
<= mReadEnd
);
394 MOZ_ASSERT(mReadEnd
- mReadBegin
<= 0x7FFFFFFF); // no more than int32_max
395 return mReadEnd
- mReadBegin
;
398 bool NamedPipeInfo::Sync(uint32_t aTimeout
) {
399 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
400 if (!mHasPendingWrite
) {
403 return WaitForSingleObject(mWriteOverlapped
.hEvent
, aTimeout
) ==
407 void NamedPipeInfo::SetNonblocking(bool nonblocking
) {
408 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
409 mNonblocking
= nonblocking
;
412 bool NamedPipeInfo::IsConnected() const {
413 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
414 return mPipe
&& mPipe
!= INVALID_HANDLE_VALUE
;
417 bool NamedPipeInfo::IsNonblocking() const {
418 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
423 NamedPipeInfo::GetHandle() const {
424 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
428 int16_t NamedPipeInfo::GetPollFlags(int16_t aInFlags
, int16_t* aOutFlags
) {
429 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
433 if (aInFlags
& PR_POLL_READ
) {
434 int32_t bytesToRead
= 0;
435 if (mReadBegin
< mReadEnd
) { // data in buffer and is ready to be read
436 bytesToRead
= Available();
437 } else if (mHasPendingRead
) { // nonblocking I/O and has pending task
438 bytesToRead
= DoReadContinue();
439 } else { // read bufer is empty.
440 bytesToRead
= DoRead();
443 if (bytesToRead
> 0) {
444 *aOutFlags
|= PR_POLL_READ
;
445 } else if (bytesToRead
< 0) {
446 *aOutFlags
|= PR_POLL_ERR
;
450 if (aInFlags
& PR_POLL_WRITE
) {
451 int32_t bytesWritten
= 0;
452 if (mHasPendingWrite
) { // nonblocking I/O and has pending task.
453 bytesWritten
= DoWriteContinue();
454 } else if (mWriteBegin
< mWriteEnd
) { // data in buffer, ready to write
455 bytesWritten
= DoWrite();
456 } else { // write buffer is empty.
457 *aOutFlags
|= PR_POLL_WRITE
;
460 if (bytesWritten
< 0) {
461 *aOutFlags
|= PR_POLL_ERR
;
462 } else if (bytesWritten
&& !mHasPendingWrite
&& mWriteBegin
== mWriteEnd
) {
463 *aOutFlags
|= PR_POLL_WRITE
;
470 // @return: data has been read and is available
471 int32_t NamedPipeInfo::DoRead() {
472 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
473 MOZ_ASSERT(!mHasPendingRead
);
474 MOZ_ASSERT(mReadBegin
== mReadEnd
); // the buffer should be empty
479 BOOL success
= ReadFile(mPipe
, mReadBuffer
, sizeof(mReadBuffer
), &mReadEnd
,
480 IsNonblocking() ? &mReadOverlapped
: nullptr);
483 LOG_NPIO_DEBUG("[%s][%p] %lu bytes read", __func__
, this, mReadEnd
);
487 switch (GetLastError()) {
488 case ERROR_MORE_DATA
: // has more data to read
489 mHasPendingRead
= true;
490 return DoReadContinue();
492 case ERROR_IO_PENDING
: // read is pending
493 mHasPendingRead
= true;
497 LOG_NPIO_ERROR("[%s] ReadFile failed (%lu)", __func__
, GetLastError());
499 PR_SetError(PR_IO_ERROR
, 0);
506 int32_t NamedPipeInfo::DoReadContinue() {
507 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
508 MOZ_ASSERT(mHasPendingRead
);
509 MOZ_ASSERT(mReadBegin
== 0 && mReadEnd
== 0);
512 success
= GetOverlappedResult(mPipe
, &mReadOverlapped
, &mReadEnd
, FALSE
);
514 mHasPendingRead
= false;
517 PR_SetError(PR_NOT_CONNECTED_ERROR
, 0);
521 LOG_NPIO_DEBUG("[%s][%p] %lu bytes read", __func__
, this, mReadEnd
);
525 switch (GetLastError()) {
526 case ERROR_MORE_DATA
:
527 mHasPendingRead
= false;
528 LOG_NPIO_DEBUG("[%s][%p] %lu bytes read", __func__
, this, mReadEnd
);
530 case ERROR_IO_INCOMPLETE
: // still in progress
533 LOG_NPIO_ERROR("[%s]: GetOverlappedResult failed (%lu)", __func__
,
536 PR_SetError(PR_IO_ERROR
, 0);
543 int32_t NamedPipeInfo::DoWrite() {
544 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
545 MOZ_ASSERT(!mHasPendingWrite
);
546 MOZ_ASSERT(mWriteBegin
< mWriteEnd
);
548 DWORD bytesWritten
= 0;
550 WriteFile(mPipe
, &mWriteBuffer
[mWriteBegin
], mWriteEnd
- mWriteBegin
,
551 &bytesWritten
, IsNonblocking() ? &mWriteOverlapped
: nullptr);
554 mWriteBegin
+= bytesWritten
;
555 LOG_NPIO_DEBUG("[%s][%p] %lu bytes written", __func__
, this, bytesWritten
);
559 if (GetLastError() != ERROR_IO_PENDING
) {
560 LOG_NPIO_ERROR("[%s] WriteFile failed (%lu)", __func__
, GetLastError());
562 PR_SetError(PR_IO_ERROR
, 0);
566 mHasPendingWrite
= true;
571 int32_t NamedPipeInfo::DoWriteContinue() {
572 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
573 MOZ_ASSERT(mHasPendingWrite
);
575 DWORD bytesWritten
= 0;
577 GetOverlappedResult(mPipe
, &mWriteOverlapped
, &bytesWritten
, FALSE
);
580 if (GetLastError() == ERROR_IO_INCOMPLETE
) {
585 LOG_NPIO_ERROR("[%s] GetOverlappedResult failed (%lu)", __func__
,
588 PR_SetError(PR_IO_ERROR
, 0);
592 mHasPendingWrite
= false;
593 mWriteBegin
+= bytesWritten
;
594 LOG_NPIO_DEBUG("[%s][%p] %lu bytes written", __func__
, this, bytesWritten
);
598 static inline NamedPipeInfo
* GetNamedPipeInfo(PRFileDesc
* aFd
) {
599 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
600 MOZ_DIAGNOSTIC_ASSERT(aFd
);
601 MOZ_DIAGNOSTIC_ASSERT(aFd
->secret
);
602 MOZ_DIAGNOSTIC_ASSERT(PR_GetLayersIdentity(aFd
) == nsNamedPipeLayerIdentity
);
604 if (!aFd
|| !aFd
->secret
||
605 PR_GetLayersIdentity(aFd
) != nsNamedPipeLayerIdentity
) {
606 LOG_NPIO_ERROR("cannot get named pipe info");
610 return reinterpret_cast<NamedPipeInfo
*>(aFd
->secret
);
613 static PRStatus
nsNamedPipeConnect(PRFileDesc
* aFd
, const PRNetAddr
* aAddr
,
614 PRIntervalTime aTimeout
) {
615 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
617 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
619 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
624 if (NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(aAddr
->local
.path
),
626 PR_SetError(PR_OUT_OF_MEMORY_ERROR
, 0);
629 if (NS_WARN_IF(NS_FAILED(info
->Connect(path
)))) {
636 static PRStatus
nsNamedPipeConnectContinue(PRFileDesc
* aFd
, PRInt16 aOutFlags
) {
637 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
642 static PRStatus
nsNamedPipeClose(PRFileDesc
* aFd
) {
643 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
645 if (aFd
->secret
&& PR_GetLayersIdentity(aFd
) == nsNamedPipeLayerIdentity
) {
646 RefPtr
<NamedPipeInfo
> info
= dont_AddRef(GetNamedPipeInfo(aFd
));
648 aFd
->secret
= nullptr;
649 aFd
->identity
= PR_INVALID_IO_LAYER
;
652 MOZ_ASSERT(!aFd
->lower
);
653 PR_Free(aFd
); // PRFileDescs are allocated with PR_Malloc().
658 static PRInt32
nsNamedPipeSend(PRFileDesc
* aFd
, const void* aBuffer
,
659 PRInt32 aAmount
, PRIntn aFlags
,
660 PRIntervalTime aTimeout
) {
661 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
666 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
668 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
671 return info
->Write(aBuffer
, aAmount
);
674 static PRInt32
nsNamedPipeRecv(PRFileDesc
* aFd
, void* aBuffer
, PRInt32 aAmount
,
675 PRIntn aFlags
, PRIntervalTime aTimeout
) {
676 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
680 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
682 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
687 if (aFlags
!= PR_MSG_PEEK
) {
688 PR_SetError(PR_UNKNOWN_ERROR
, 0);
691 return info
->Peek(aBuffer
, aAmount
);
694 return info
->Read(aBuffer
, aAmount
);
697 static inline PRInt32
nsNamedPipeRead(PRFileDesc
* aFd
, void* aBuffer
,
699 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
701 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
703 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
706 return info
->Read(aBuffer
, aAmount
);
709 static inline PRInt32
nsNamedPipeWrite(PRFileDesc
* aFd
, const void* aBuffer
,
711 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
713 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
715 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
718 return info
->Write(aBuffer
, aAmount
);
721 static PRInt32
nsNamedPipeAvailable(PRFileDesc
* aFd
) {
722 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
724 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
726 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
729 return static_cast<PRInt32
>(info
->Available());
732 static PRInt64
nsNamedPipeAvailable64(PRFileDesc
* aFd
) {
733 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
735 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
737 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
740 return static_cast<PRInt64
>(info
->Available());
743 static PRStatus
nsNamedPipeSync(PRFileDesc
* aFd
) {
744 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
746 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
748 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
751 return info
->Sync(0) ? PR_SUCCESS
: PR_FAILURE
;
754 static PRInt16
nsNamedPipePoll(PRFileDesc
* aFd
, PRInt16 aInFlags
,
755 PRInt16
* aOutFlags
) {
756 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
758 NamedPipeInfo
* info
= GetNamedPipeInfo(aFd
);
760 PR_SetError(PR_BAD_DESCRIPTOR_ERROR
, 0);
763 return info
->GetPollFlags(aInFlags
, aOutFlags
);
766 // FIXME: remove socket option functions?
767 static PRStatus
nsNamedPipeGetSocketOption(PRFileDesc
* aFd
,
768 PRSocketOptionData
* aData
) {
769 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
774 switch (aData
->option
) {
775 case PR_SockOpt_Nonblocking
:
776 aData
->value
.non_blocking
=
777 GetNamedPipeInfo(aFd
)->IsNonblocking() ? PR_TRUE
: PR_FALSE
;
779 case PR_SockOpt_Keepalive
:
780 aData
->value
.keep_alive
= PR_TRUE
;
782 case PR_SockOpt_NoDelay
:
783 aData
->value
.no_delay
= PR_TRUE
;
786 PR_SetError(PR_INVALID_METHOD_ERROR
, 0);
793 static PRStatus
nsNamedPipeSetSocketOption(PRFileDesc
* aFd
,
794 const PRSocketOptionData
* aData
) {
795 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
800 switch (aData
->option
) {
801 case PR_SockOpt_Nonblocking
:
802 GetNamedPipeInfo(aFd
)->SetNonblocking(aData
->value
.non_blocking
);
804 case PR_SockOpt_Keepalive
:
805 case PR_SockOpt_NoDelay
:
808 PR_SetError(PR_INVALID_METHOD_ERROR
, 0);
815 static void Initialize() {
816 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
818 static bool initialized
= false;
823 nsNamedPipeLayerIdentity
= PR_GetUniqueIdentity("Named Pipe layer");
824 nsNamedPipeLayerMethods
= *PR_GetDefaultIOMethods();
825 nsNamedPipeLayerMethods
.close
= nsNamedPipeClose
;
826 nsNamedPipeLayerMethods
.read
= nsNamedPipeRead
;
827 nsNamedPipeLayerMethods
.write
= nsNamedPipeWrite
;
828 nsNamedPipeLayerMethods
.available
= nsNamedPipeAvailable
;
829 nsNamedPipeLayerMethods
.available64
= nsNamedPipeAvailable64
;
830 nsNamedPipeLayerMethods
.fsync
= nsNamedPipeSync
;
831 nsNamedPipeLayerMethods
.connect
= nsNamedPipeConnect
;
832 nsNamedPipeLayerMethods
.recv
= nsNamedPipeRecv
;
833 nsNamedPipeLayerMethods
.send
= nsNamedPipeSend
;
834 nsNamedPipeLayerMethods
.poll
= nsNamedPipePoll
;
835 nsNamedPipeLayerMethods
.getsocketoption
= nsNamedPipeGetSocketOption
;
836 nsNamedPipeLayerMethods
.setsocketoption
= nsNamedPipeSetSocketOption
;
837 nsNamedPipeLayerMethods
.connectcontinue
= nsNamedPipeConnectContinue
;
842 bool IsNamedPipePath(const nsACString
& aPath
) {
843 return StringBeginsWith(aPath
, "\\\\.\\pipe\\"_ns
);
846 PRFileDesc
* CreateNamedPipeLayer() {
847 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
851 PR_CreateIOLayerStub(nsNamedPipeLayerIdentity
, &nsNamedPipeLayerMethods
);
852 if (NS_WARN_IF(!layer
)) {
853 LOG_NPIO_ERROR("CreateNamedPipeLayer() failed.");
857 RefPtr
<NamedPipeInfo
> info
= new NamedPipeInfo();
858 layer
->secret
= reinterpret_cast<PRFilePrivate
*>(info
.forget().take());
864 } // namespace mozilla