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