nsSyncStreamListener.cpp (5176B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "mozilla/SpinEventLoopUntil.h" 6 #include "nsIOService.h" 7 #include "nsIPipe.h" 8 #include "nsSyncStreamListener.h" 9 #include "nsThreadUtils.h" 10 #include <algorithm> 11 12 using namespace mozilla::net; 13 14 nsSyncStreamListener::nsSyncStreamListener() { 15 MOZ_ASSERT(NS_IsMainThread()); 16 NS_NewPipe(getter_AddRefs(mPipeIn), getter_AddRefs(mPipeOut), 17 mozilla::net::nsIOService::gDefaultSegmentSize, 18 UINT32_MAX, // no size limit 19 false, false); 20 } 21 22 nsresult nsSyncStreamListener::WaitForData() { 23 mKeepWaiting = true; 24 25 if (!mozilla::SpinEventLoopUntil("nsSyncStreamListener::Create"_ns, 26 [&]() { return !mKeepWaiting; })) { 27 return NS_ERROR_FAILURE; 28 } 29 30 return NS_OK; 31 } 32 33 //----------------------------------------------------------------------------- 34 // nsSyncStreamListener::nsISupports 35 //----------------------------------------------------------------------------- 36 37 NS_IMPL_ISUPPORTS(nsSyncStreamListener, nsIStreamListener, nsIRequestObserver, 38 nsIInputStream, nsISyncStreamListener) 39 40 //----------------------------------------------------------------------------- 41 // nsSyncStreamListener::nsISyncStreamListener 42 //----------------------------------------------------------------------------- 43 44 NS_IMETHODIMP 45 nsSyncStreamListener::GetInputStream(nsIInputStream** result) { 46 *result = do_AddRef(this).take(); 47 return NS_OK; 48 } 49 50 //----------------------------------------------------------------------------- 51 // nsSyncStreamListener::nsIStreamListener 52 //----------------------------------------------------------------------------- 53 54 NS_IMETHODIMP 55 nsSyncStreamListener::OnStartRequest(nsIRequest* request) { return NS_OK; } 56 57 NS_IMETHODIMP 58 nsSyncStreamListener::OnDataAvailable(nsIRequest* request, 59 nsIInputStream* stream, uint64_t offset, 60 uint32_t count) { 61 uint32_t bytesWritten; 62 63 nsresult rv = mPipeOut->WriteFrom(stream, count, &bytesWritten); 64 65 // if we get an error, then return failure. this will cause the 66 // channel to be canceled, and as a result our OnStopRequest method 67 // will be called immediately. because of this we do not need to 68 // set mStatus or mKeepWaiting here. 69 if (NS_FAILED(rv)) return rv; 70 71 // we expect that all data will be written to the pipe because 72 // the pipe was created to have "infinite" room. 73 NS_ASSERTION(bytesWritten == count, "did not write all data"); 74 75 mKeepWaiting = false; // unblock Read 76 return NS_OK; 77 } 78 79 NS_IMETHODIMP 80 nsSyncStreamListener::OnStopRequest(nsIRequest* request, nsresult status) { 81 mStatus = status; 82 mKeepWaiting = false; // unblock Read 83 mDone = true; 84 return NS_OK; 85 } 86 87 //----------------------------------------------------------------------------- 88 // nsSyncStreamListener::nsIInputStream 89 //----------------------------------------------------------------------------- 90 91 NS_IMETHODIMP 92 nsSyncStreamListener::Close() { 93 mStatus = NS_BASE_STREAM_CLOSED; 94 mDone = true; 95 96 // It'd be nice if we could explicitly cancel the request at this point, 97 // but we don't have a reference to it, so the best we can do is close the 98 // pipe so that the next OnDataAvailable event will fail. 99 if (mPipeIn) { 100 mPipeIn->Close(); 101 mPipeIn = nullptr; 102 } 103 return NS_OK; 104 } 105 106 NS_IMETHODIMP 107 nsSyncStreamListener::Available(uint64_t* result) { 108 if (NS_FAILED(mStatus)) return mStatus; 109 110 mStatus = mPipeIn->Available(result); 111 if (NS_SUCCEEDED(mStatus) && (*result == 0) && !mDone) { 112 nsresult rv = WaitForData(); 113 if (NS_FAILED(rv)) { 114 // Note that `WaitForData` could fail `mStatus`. Do not overwrite if it's 115 // the case. 116 mStatus = NS_SUCCEEDED(mStatus) ? rv : mStatus; 117 } else if (NS_SUCCEEDED(mStatus)) { 118 mStatus = mPipeIn->Available(result); 119 } 120 } 121 return mStatus; 122 } 123 124 NS_IMETHODIMP 125 nsSyncStreamListener::StreamStatus() { 126 if (NS_FAILED(mStatus)) { 127 return mStatus; 128 } 129 130 mStatus = mPipeIn->StreamStatus(); 131 return mStatus; 132 } 133 134 NS_IMETHODIMP 135 nsSyncStreamListener::Read(char* buf, uint32_t bufLen, uint32_t* result) { 136 if (mStatus == NS_BASE_STREAM_CLOSED) { 137 *result = 0; 138 return NS_OK; 139 } 140 141 uint64_t avail64; 142 if (NS_FAILED(Available(&avail64))) return mStatus; 143 144 uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)bufLen); 145 mStatus = mPipeIn->Read(buf, avail, result); 146 return mStatus; 147 } 148 149 NS_IMETHODIMP 150 nsSyncStreamListener::ReadSegments(nsWriteSegmentFun writer, void* closure, 151 uint32_t count, uint32_t* result) { 152 if (mStatus == NS_BASE_STREAM_CLOSED) { 153 *result = 0; 154 return NS_OK; 155 } 156 157 uint64_t avail64; 158 if (NS_FAILED(Available(&avail64))) return mStatus; 159 160 uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)count); 161 mStatus = mPipeIn->ReadSegments(writer, closure, avail, result); 162 return mStatus; 163 } 164 165 NS_IMETHODIMP 166 nsSyncStreamListener::IsNonBlocking(bool* result) { 167 *result = false; 168 return NS_OK; 169 }