1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include <stdlib.h>
40 : #include "prlog.h"
41 :
42 : #include "mozilla/Mutex.h"
43 : #include "nsIInputStreamTee.h"
44 : #include "nsIInputStream.h"
45 : #include "nsIOutputStream.h"
46 : #include "nsCOMPtr.h"
47 : #include "nsAutoPtr.h"
48 : #include "nsIEventTarget.h"
49 : #include "nsThreadUtils.h"
50 :
51 : using namespace mozilla;
52 :
53 : #ifdef PR_LOGGING
54 1464 : static PRLogModuleInfo* gInputStreamTeeLog = PR_NewLogModule("nsInputStreamTee");
55 : #define LOG(args) PR_LOG(gInputStreamTeeLog, PR_LOG_DEBUG, args)
56 : #else
57 : #define LOG(args)
58 : #endif
59 :
60 : class nsInputStreamTee : public nsIInputStreamTee
61 : {
62 : public:
63 : NS_DECL_ISUPPORTS
64 : NS_DECL_NSIINPUTSTREAM
65 : NS_DECL_NSIINPUTSTREAMTEE
66 :
67 : nsInputStreamTee();
68 : bool SinkIsValid();
69 : void InvalidateSink();
70 :
71 : private:
72 1301 : ~nsInputStreamTee() {}
73 :
74 : nsresult TeeSegment(const char *buf, PRUint32 count);
75 :
76 : static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *,
77 : PRUint32, PRUint32, PRUint32 *);
78 :
79 : private:
80 : nsCOMPtr<nsIInputStream> mSource;
81 : nsCOMPtr<nsIOutputStream> mSink;
82 : nsCOMPtr<nsIEventTarget> mEventTarget;
83 : nsWriteSegmentFun mWriter; // for implementing ReadSegments
84 : void *mClosure; // for implementing ReadSegments
85 : nsAutoPtr<Mutex> mLock; // synchronize access to mSinkIsValid
86 : bool mSinkIsValid; // False if TeeWriteEvent fails
87 : };
88 :
89 : class nsInputStreamTeeWriteEvent : public nsRunnable {
90 : public:
91 : // aTee's lock is held across construction of this object
92 1382 : nsInputStreamTeeWriteEvent(const char *aBuf, PRUint32 aCount,
93 : nsIOutputStream *aSink,
94 : nsInputStreamTee *aTee)
95 1382 : {
96 : // copy the buffer - will be free'd by dtor
97 1382 : mBuf = (char *)malloc(aCount);
98 1382 : if (mBuf) memcpy(mBuf, (char *)aBuf, aCount);
99 1382 : mCount = aCount;
100 1382 : mSink = aSink;
101 : bool isNonBlocking;
102 1382 : mSink->IsNonBlocking(&isNonBlocking);
103 1382 : NS_ASSERTION(isNonBlocking == false, "mSink is nonblocking");
104 1382 : mTee = aTee;
105 1382 : }
106 :
107 1382 : NS_IMETHOD Run()
108 : {
109 1382 : if (!mBuf) {
110 : NS_WARNING("nsInputStreamTeeWriteEvent::Run() "
111 0 : "memory not allocated\n");
112 0 : return NS_OK;
113 : }
114 1382 : NS_ABORT_IF_FALSE(mSink, "mSink is null!");
115 :
116 : // The output stream could have been invalidated between when
117 : // this event was dispatched and now, so check before writing.
118 1382 : if (!mTee->SinkIsValid()) {
119 0 : return NS_OK;
120 : }
121 :
122 1382 : LOG(("nsInputStreamTeeWriteEvent::Run() [%p]"
123 : "will write %u bytes to %p\n",
124 : this, mCount, mSink.get()));
125 :
126 1382 : PRUint32 totalBytesWritten = 0;
127 4088 : while (mCount) {
128 : nsresult rv;
129 1382 : PRUint32 bytesWritten = 0;
130 1382 : rv = mSink->Write(mBuf + totalBytesWritten, mCount, &bytesWritten);
131 1382 : if (NS_FAILED(rv)) {
132 58 : LOG(("nsInputStreamTeeWriteEvent::Run[%p] error %x in writing",
133 : this,rv));
134 58 : mTee->InvalidateSink();
135 58 : break;
136 : }
137 1324 : totalBytesWritten += bytesWritten;
138 1324 : NS_ASSERTION(bytesWritten <= mCount, "wrote too much");
139 1324 : mCount -= bytesWritten;
140 : }
141 1382 : return NS_OK;
142 : }
143 :
144 : protected:
145 2764 : virtual ~nsInputStreamTeeWriteEvent()
146 2764 : {
147 1382 : if (mBuf) free(mBuf);
148 1382 : mBuf = nsnull;
149 5528 : }
150 :
151 : private:
152 : char *mBuf;
153 : PRUint32 mCount;
154 : nsCOMPtr<nsIOutputStream> mSink;
155 : // back pointer to the tee that created this runnable
156 : nsRefPtr<nsInputStreamTee> mTee;
157 : };
158 :
159 1301 : nsInputStreamTee::nsInputStreamTee(): mLock(nsnull)
160 1301 : , mSinkIsValid(true)
161 : {
162 1301 : }
163 :
164 : bool
165 2772 : nsInputStreamTee::SinkIsValid()
166 : {
167 5544 : MutexAutoLock lock(*mLock);
168 2772 : return mSinkIsValid;
169 : }
170 :
171 : void
172 58 : nsInputStreamTee::InvalidateSink()
173 : {
174 116 : MutexAutoLock lock(*mLock);
175 58 : mSinkIsValid = false;
176 58 : }
177 :
178 : nsresult
179 1663 : nsInputStreamTee::TeeSegment(const char *buf, PRUint32 count)
180 : {
181 1663 : if (!mSink) return NS_OK; // nothing to do
182 1663 : if (mLock) { // asynchronous case
183 1390 : NS_ASSERTION(mEventTarget, "mEventTarget is null, mLock is not null.");
184 1390 : if (!SinkIsValid()) {
185 8 : return NS_OK; // nothing to do
186 : }
187 : nsRefPtr<nsIRunnable> event =
188 4146 : new nsInputStreamTeeWriteEvent(buf, count, mSink, this);
189 1382 : NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
190 1382 : LOG(("nsInputStreamTee::TeeSegment [%p] dispatching write %u bytes\n",
191 : this, count));
192 1382 : return mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
193 : } else { // synchronous case
194 273 : NS_ASSERTION(!mEventTarget, "mEventTarget is not null, mLock is null.");
195 : nsresult rv;
196 273 : PRUint32 totalBytesWritten = 0;
197 819 : while (count) {
198 273 : PRUint32 bytesWritten = 0;
199 273 : rv = mSink->Write(buf + totalBytesWritten, count, &bytesWritten);
200 273 : if (NS_FAILED(rv)) {
201 : // ok, this is not a fatal error... just drop our reference to mSink
202 : // and continue on as if nothing happened.
203 0 : NS_WARNING("Write failed (non-fatal)");
204 : // catch possible misuse of the input stream tee
205 0 : NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "sink must be a blocking stream");
206 0 : mSink = 0;
207 0 : break;
208 : }
209 273 : totalBytesWritten += bytesWritten;
210 273 : NS_ASSERTION(bytesWritten <= count, "wrote too much");
211 273 : count -= bytesWritten;
212 : }
213 273 : return NS_OK;
214 : }
215 : }
216 :
217 : NS_METHOD
218 602 : nsInputStreamTee::WriteSegmentFun(nsIInputStream *in, void *closure, const char *fromSegment,
219 : PRUint32 offset, PRUint32 count, PRUint32 *writeCount)
220 : {
221 602 : nsInputStreamTee *tee = reinterpret_cast<nsInputStreamTee *>(closure);
222 :
223 602 : nsresult rv = tee->mWriter(in, tee->mClosure, fromSegment, offset, count, writeCount);
224 602 : if (NS_FAILED(rv) || (*writeCount == 0)) {
225 6 : NS_ASSERTION((NS_FAILED(rv) ? (*writeCount == 0) : true),
226 : "writer returned an error with non-zero writeCount");
227 6 : return rv;
228 : }
229 :
230 596 : return tee->TeeSegment(fromSegment, *writeCount);
231 : }
232 :
233 38064 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTee,
234 : nsIInputStreamTee,
235 : nsIInputStream)
236 : NS_IMETHODIMP
237 0 : nsInputStreamTee::Close()
238 : {
239 0 : NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
240 0 : nsresult rv = mSource->Close();
241 0 : mSource = 0;
242 0 : mSink = 0;
243 0 : return rv;
244 : }
245 :
246 : NS_IMETHODIMP
247 360 : nsInputStreamTee::Available(PRUint32 *avail)
248 : {
249 360 : NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
250 360 : return mSource->Available(avail);
251 : }
252 :
253 : NS_IMETHODIMP
254 1067 : nsInputStreamTee::Read(char *buf, PRUint32 count, PRUint32 *bytesRead)
255 : {
256 1067 : NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
257 :
258 1067 : nsresult rv = mSource->Read(buf, count, bytesRead);
259 1067 : if (NS_FAILED(rv) || (*bytesRead == 0))
260 0 : return rv;
261 :
262 1067 : return TeeSegment(buf, *bytesRead);
263 : }
264 :
265 : NS_IMETHODIMP
266 602 : nsInputStreamTee::ReadSegments(nsWriteSegmentFun writer,
267 : void *closure,
268 : PRUint32 count,
269 : PRUint32 *bytesRead)
270 : {
271 602 : NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
272 :
273 602 : mWriter = writer;
274 602 : mClosure = closure;
275 :
276 602 : return mSource->ReadSegments(WriteSegmentFun, this, count, bytesRead);
277 : }
278 :
279 : NS_IMETHODIMP
280 0 : nsInputStreamTee::IsNonBlocking(bool *result)
281 : {
282 0 : NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
283 0 : return mSource->IsNonBlocking(result);
284 : }
285 :
286 : NS_IMETHODIMP
287 1648 : nsInputStreamTee::SetSource(nsIInputStream *source)
288 : {
289 1648 : mSource = source;
290 1648 : return NS_OK;
291 : }
292 :
293 : NS_IMETHODIMP
294 0 : nsInputStreamTee::GetSource(nsIInputStream **source)
295 : {
296 0 : NS_IF_ADDREF(*source = mSource);
297 0 : return NS_OK;
298 : }
299 :
300 : NS_IMETHODIMP
301 2602 : nsInputStreamTee::SetSink(nsIOutputStream *sink)
302 : {
303 : #ifdef DEBUG
304 2602 : if (sink) {
305 : bool nonBlocking;
306 1301 : nsresult rv = sink->IsNonBlocking(&nonBlocking);
307 1301 : if (NS_FAILED(rv) || nonBlocking)
308 0 : NS_ERROR("sink should be a blocking stream");
309 : }
310 : #endif
311 2602 : mSink = sink;
312 2602 : return NS_OK;
313 : }
314 :
315 : NS_IMETHODIMP
316 0 : nsInputStreamTee::GetSink(nsIOutputStream **sink)
317 : {
318 0 : NS_IF_ADDREF(*sink = mSink);
319 0 : return NS_OK;
320 : }
321 :
322 : NS_IMETHODIMP
323 1301 : nsInputStreamTee::SetEventTarget(nsIEventTarget *anEventTarget)
324 : {
325 1301 : mEventTarget = anEventTarget;
326 1301 : if (mEventTarget) {
327 : // Only need synchronization if this is an async tee
328 1028 : mLock = new Mutex("nsInputStreamTee.mLock");
329 : }
330 1301 : return NS_OK;
331 : }
332 :
333 : NS_IMETHODIMP
334 0 : nsInputStreamTee::GetEventTarget(nsIEventTarget **anEventTarget)
335 : {
336 0 : NS_IF_ADDREF(*anEventTarget = mEventTarget);
337 0 : return NS_OK;
338 : }
339 :
340 :
341 : nsresult
342 1301 : NS_NewInputStreamTeeAsync(nsIInputStream **result,
343 : nsIInputStream *source,
344 : nsIOutputStream *sink,
345 : nsIEventTarget *anEventTarget)
346 : {
347 : nsresult rv;
348 :
349 2602 : nsCOMPtr<nsIInputStreamTee> tee = new nsInputStreamTee();
350 1301 : if (!tee)
351 0 : return NS_ERROR_OUT_OF_MEMORY;
352 :
353 1301 : rv = tee->SetSource(source);
354 1301 : if (NS_FAILED(rv)) return rv;
355 :
356 1301 : rv = tee->SetSink(sink);
357 1301 : if (NS_FAILED(rv)) return rv;
358 :
359 1301 : rv = tee->SetEventTarget(anEventTarget);
360 1301 : if (NS_FAILED(rv)) return rv;
361 :
362 1301 : NS_ADDREF(*result = tee);
363 1301 : return rv;
364 : }
365 :
366 : nsresult
367 273 : NS_NewInputStreamTee(nsIInputStream **result,
368 : nsIInputStream *source,
369 : nsIOutputStream *sink)
370 : {
371 273 : return NS_NewInputStreamTeeAsync(result, source, sink, nsnull);
372 4392 : }
|