summaryrefslogtreecommitdiffstats
path: root/netwerk/base/nsSyncStreamListener.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/base/nsSyncStreamListener.cpp')
-rw-r--r--netwerk/base/nsSyncStreamListener.cpp169
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;
+}