diff options
Diffstat (limited to 'netwerk/base/nsSyncStreamListener.cpp')
-rw-r--r-- | netwerk/base/nsSyncStreamListener.cpp | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/netwerk/base/nsSyncStreamListener.cpp b/netwerk/base/nsSyncStreamListener.cpp new file mode 100644 index 0000000000..7e5ec94231 --- /dev/null +++ b/netwerk/base/nsSyncStreamListener.cpp @@ -0,0 +1,169 @@ +/* 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 "mozilla/SpinEventLoopUntil.h" +#include "nsIOService.h" +#include "nsIPipe.h" +#include "nsSyncStreamListener.h" +#include "nsThreadUtils.h" +#include <algorithm> + +using namespace mozilla::net; + +nsSyncStreamListener::nsSyncStreamListener() { + MOZ_ASSERT(NS_IsMainThread()); + NS_NewPipe(getter_AddRefs(mPipeIn), getter_AddRefs(mPipeOut), + mozilla::net::nsIOService::gDefaultSegmentSize, + UINT32_MAX, // no size limit + false, false); +} + +nsresult nsSyncStreamListener::WaitForData() { + mKeepWaiting = true; + + if (!mozilla::SpinEventLoopUntil("nsSyncStreamListener::Create"_ns, + [&]() { return !mKeepWaiting; })) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsSyncStreamListener::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(nsSyncStreamListener, nsIStreamListener, nsIRequestObserver, + nsIInputStream, nsISyncStreamListener) + +//----------------------------------------------------------------------------- +// nsSyncStreamListener::nsISyncStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsSyncStreamListener::GetInputStream(nsIInputStream** result) { + *result = do_AddRef(this).take(); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsSyncStreamListener::nsIStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsSyncStreamListener::OnStartRequest(nsIRequest* request) { return NS_OK; } + +NS_IMETHODIMP +nsSyncStreamListener::OnDataAvailable(nsIRequest* request, + nsIInputStream* stream, uint64_t offset, + uint32_t count) { + uint32_t bytesWritten; + + nsresult rv = mPipeOut->WriteFrom(stream, count, &bytesWritten); + + // if we get an error, then return failure. this will cause the + // channel to be canceled, and as a result our OnStopRequest method + // will be called immediately. because of this we do not need to + // set mStatus or mKeepWaiting here. + if (NS_FAILED(rv)) return rv; + + // we expect that all data will be written to the pipe because + // the pipe was created to have "infinite" room. + NS_ASSERTION(bytesWritten == count, "did not write all data"); + + mKeepWaiting = false; // unblock Read + return NS_OK; +} + +NS_IMETHODIMP +nsSyncStreamListener::OnStopRequest(nsIRequest* request, nsresult status) { + mStatus = status; + mKeepWaiting = false; // unblock Read + mDone = true; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsSyncStreamListener::nsIInputStream +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsSyncStreamListener::Close() { + mStatus = NS_BASE_STREAM_CLOSED; + mDone = true; + + // It'd be nice if we could explicitly cancel the request at this point, + // but we don't have a reference to it, so the best we can do is close the + // pipe so that the next OnDataAvailable event will fail. + if (mPipeIn) { + mPipeIn->Close(); + mPipeIn = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsSyncStreamListener::Available(uint64_t* result) { + if (NS_FAILED(mStatus)) return mStatus; + + mStatus = mPipeIn->Available(result); + if (NS_SUCCEEDED(mStatus) && (*result == 0) && !mDone) { + nsresult rv = WaitForData(); + if (NS_FAILED(rv)) { + // Note that `WaitForData` could fail `mStatus`. Do not overwrite if it's + // the case. + mStatus = NS_SUCCEEDED(mStatus) ? rv : mStatus; + } else if (NS_SUCCEEDED(mStatus)) { + mStatus = mPipeIn->Available(result); + } + } + return mStatus; +} + +NS_IMETHODIMP +nsSyncStreamListener::StreamStatus() { + if (NS_FAILED(mStatus)) { + return mStatus; + } + + mStatus = mPipeIn->StreamStatus(); + return mStatus; +} + +NS_IMETHODIMP +nsSyncStreamListener::Read(char* buf, uint32_t bufLen, uint32_t* result) { + if (mStatus == NS_BASE_STREAM_CLOSED) { + *result = 0; + return NS_OK; + } + + uint64_t avail64; + if (NS_FAILED(Available(&avail64))) return mStatus; + + uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)bufLen); + mStatus = mPipeIn->Read(buf, avail, result); + return mStatus; +} + +NS_IMETHODIMP +nsSyncStreamListener::ReadSegments(nsWriteSegmentFun writer, void* closure, + uint32_t count, uint32_t* result) { + if (mStatus == NS_BASE_STREAM_CLOSED) { + *result = 0; + return NS_OK; + } + + uint64_t avail64; + if (NS_FAILED(Available(&avail64))) return mStatus; + + uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)count); + mStatus = mPipeIn->ReadSegments(writer, closure, avail, result); + return mStatus; +} + +NS_IMETHODIMP +nsSyncStreamListener::IsNonBlocking(bool* result) { + *result = false; + return NS_OK; +} |