~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/netwerk/base/src/nsStreamTransportService.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ***** BEGIN LICENSE BLOCK *****
 
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
3
 *
 
4
 * The contents of this file are subject to the Mozilla Public License Version
 
5
 * 1.1 (the "License"); you may not use this file except in compliance with
 
6
 * the License. You may obtain a copy of the License at
 
7
 * http://www.mozilla.org/MPL/
 
8
 *
 
9
 * Software distributed under the License is distributed on an "AS IS" basis,
 
10
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
11
 * for the specific language governing rights and limitations under the
 
12
 * License.
 
13
 *
 
14
 * The Original Code is Mozilla.
 
15
 *
 
16
 * The Initial Developer of the Original Code is
 
17
 * Netscape Communications Corporation.
 
18
 * Portions created by the Initial Developer are Copyright (C) 2002
 
19
 * the Initial Developer. All Rights Reserved.
 
20
 *
 
21
 * Contributor(s):
 
22
 *   Darin Fisher <darin@netscape.com>
 
23
 *
 
24
 * Alternatively, the contents of this file may be used under the terms of
 
25
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
26
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
27
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
28
 * of those above. If you wish to allow use of your version of this file only
 
29
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
30
 * use your version of this file under the terms of the MPL, indicate your
 
31
 * decision by deleting the provisions above and replace them with the notice
 
32
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
33
 * the provisions above, a recipient may use your version of this file under
 
34
 * the terms of any one of the MPL, the GPL or the LGPL.
 
35
 *
 
36
 * ***** END LICENSE BLOCK ***** */
 
37
 
 
38
#include "nsStreamTransportService.h"
 
39
#include "nsNetSegmentUtils.h"
 
40
#include "nsAutoLock.h"
 
41
#include "nsTransportUtils.h"
 
42
#include "nsStreamUtils.h"
 
43
#include "nsNetError.h"
 
44
#include "nsNetCID.h"
 
45
 
 
46
#include "nsIServiceManager.h"
 
47
#include "nsIAsyncInputStream.h"
 
48
#include "nsIAsyncOutputStream.h"
 
49
#include "nsISeekableStream.h"
 
50
#include "nsIPipe.h"
 
51
#include "nsITransport.h"
 
52
#include "nsIRunnable.h"
 
53
#include "nsIProxyObjectManager.h"
 
54
#include "nsIEventTarget.h"
 
55
 
 
56
//-----------------------------------------------------------------------------
 
57
// nsInputStreamTransport
 
58
//
 
59
// Implements nsIInputStream as a wrapper around the real input stream.  This
 
60
// allows the transport to support seeking, range-limiting, progress reporting,
 
61
// and close-when-done semantics while utilizing NS_AsyncCopy.
 
62
//-----------------------------------------------------------------------------
 
63
 
 
64
class nsInputStreamTransport : public nsITransport
 
65
                             , public nsIInputStream
 
66
{
 
67
public:
 
68
    NS_DECL_ISUPPORTS
 
69
    NS_DECL_NSITRANSPORT
 
70
    NS_DECL_NSIINPUTSTREAM
 
71
 
 
72
    nsInputStreamTransport(nsIInputStream *source,
 
73
                           PRUint32 offset,
 
74
                           PRUint32 limit,
 
75
                           PRBool closeWhenDone)
 
76
        : mSource(source)
 
77
        , mOffset(offset)
 
78
        , mLimit(limit)
 
79
        , mCloseWhenDone(closeWhenDone)
 
80
        , mFirstTime(PR_TRUE)
 
81
        , mInProgress(PR_FALSE)
 
82
    {
 
83
    }
 
84
 
 
85
    virtual ~nsInputStreamTransport()
 
86
    {
 
87
    }
 
88
 
 
89
private:
 
90
    nsCOMPtr<nsIAsyncInputStream>   mPipeIn;
 
91
 
 
92
    // while the copy is active, these members may only be accessed from the
 
93
    // nsIInputStream implementation.
 
94
    nsCOMPtr<nsITransportEventSink> mEventSink;
 
95
    nsCOMPtr<nsIInputStream>        mSource;
 
96
    PRUint32                        mOffset;
 
97
    PRUint32                        mLimit;
 
98
    PRPackedBool                    mCloseWhenDone;
 
99
    PRPackedBool                    mFirstTime;
 
100
 
 
101
    // this variable serves as a lock to prevent the state of the transport
 
102
    // from being modified once the copy is in progress.
 
103
    PRPackedBool                    mInProgress;
 
104
};
 
105
 
 
106
NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTransport,
 
107
                              nsITransport,
 
108
                              nsIInputStream)
 
109
 
 
110
/** nsITransport **/
 
111
 
 
112
NS_IMETHODIMP
 
113
nsInputStreamTransport::OpenInputStream(PRUint32 flags,
 
114
                                        PRUint32 segsize,
 
115
                                        PRUint32 segcount,
 
116
                                        nsIInputStream **result)
 
117
{
 
118
    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
 
119
 
 
120
    nsresult rv;
 
121
    nsCOMPtr<nsIEventTarget> target =
 
122
            do_GetService(NS_IOTHREADPOOL_CONTRACTID, &rv);
 
123
    if (NS_FAILED(rv)) return rv;
 
124
 
 
125
    // XXX if the caller requests an unbuffered stream, then perhaps
 
126
    //     we'd want to simply return mSource; however, then we would
 
127
    //     not be reading mSource on a background thread.  is this ok?
 
128
 
 
129
    PRBool nonblocking = !(flags & OPEN_BLOCKING);
 
130
 
 
131
    net_ResolveSegmentParams(segsize, segcount);
 
132
    nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
 
133
 
 
134
    nsCOMPtr<nsIAsyncOutputStream> pipeOut;
 
135
    rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
 
136
                     getter_AddRefs(pipeOut),
 
137
                     nonblocking, PR_TRUE,
 
138
                     segsize, segcount, segalloc);
 
139
    if (NS_FAILED(rv)) return rv;
 
140
 
 
141
    mInProgress = PR_TRUE;
 
142
 
 
143
    // startup async copy process...
 
144
    rv = NS_AsyncCopy(this, pipeOut, target,
 
145
                      NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
 
146
    if (NS_SUCCEEDED(rv))
 
147
        NS_ADDREF(*result = mPipeIn);
 
148
 
 
149
    return rv;
 
150
}
 
151
 
 
152
NS_IMETHODIMP
 
153
nsInputStreamTransport::OpenOutputStream(PRUint32 flags,
 
154
                                         PRUint32 segsize,
 
155
                                         PRUint32 segcount,
 
156
                                         nsIOutputStream **result)
 
157
{
 
158
    // this transport only supports reading!
 
159
    NS_NOTREACHED("nsInputStreamTransport::OpenOutputStream");
 
160
    return NS_ERROR_UNEXPECTED;
 
161
}
 
162
 
 
163
NS_IMETHODIMP
 
164
nsInputStreamTransport::Close(nsresult reason)
 
165
{
 
166
    if (NS_SUCCEEDED(reason))
 
167
        reason = NS_BASE_STREAM_CLOSED;
 
168
 
 
169
    return mPipeIn->CloseWithStatus(reason);
 
170
}
 
171
 
 
172
NS_IMETHODIMP
 
173
nsInputStreamTransport::SetEventSink(nsITransportEventSink *sink,
 
174
                                     nsIEventTarget *target)
 
175
{
 
176
    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
 
177
 
 
178
    if (target)
 
179
        return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
 
180
                                              sink, target);
 
181
 
 
182
    mEventSink = sink;
 
183
    return NS_OK;
 
184
}
 
185
 
 
186
/** nsIInputStream **/
 
187
 
 
188
NS_IMETHODIMP
 
189
nsInputStreamTransport::Close()
 
190
{
 
191
    if (mCloseWhenDone)
 
192
        mSource->Close();
 
193
 
 
194
    // make additional reads return early...
 
195
    mOffset = mLimit = 0;
 
196
    return NS_OK;
 
197
}
 
198
 
 
199
NS_IMETHODIMP
 
200
nsInputStreamTransport::Available(PRUint32 *result)
 
201
{
 
202
    return NS_ERROR_NOT_IMPLEMENTED;
 
203
}
 
204
 
 
205
NS_IMETHODIMP
 
206
nsInputStreamTransport::Read(char *buf, PRUint32 count, PRUint32 *result)
 
207
{
 
208
    if (mFirstTime) {
 
209
        mFirstTime = PR_FALSE;
 
210
        if (mOffset) {
 
211
            // read from current position if offset equal to max
 
212
            if (mOffset != PR_UINT32_MAX) {
 
213
                nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSource);
 
214
                if (seekable)
 
215
                    seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
 
216
            }
 
217
            // reset offset to zero so we can use it to enforce limit
 
218
            mOffset = 0;
 
219
        }
 
220
    }
 
221
 
 
222
    // limit amount read
 
223
    PRUint32 max = mLimit - mOffset;
 
224
    if (max == 0) {
 
225
        *result = 0;
 
226
        return NS_OK;
 
227
    }
 
228
        
 
229
    if (count > max)
 
230
        count = max;
 
231
 
 
232
    nsresult rv = mSource->Read(buf, count, result);
 
233
 
 
234
    if (NS_SUCCEEDED(rv)) {
 
235
        mOffset += *result;
 
236
        if (mEventSink)
 
237
            mEventSink->OnTransportStatus(this, STATUS_READING, mOffset, mLimit);
 
238
    }
 
239
    return rv;
 
240
}
 
241
 
 
242
NS_IMETHODIMP
 
243
nsInputStreamTransport::ReadSegments(nsWriteSegmentFun writer, void *closure,
 
244
                                     PRUint32 count, PRUint32 *result)
 
245
{
 
246
    return NS_ERROR_NOT_IMPLEMENTED;
 
247
}
 
248
 
 
249
NS_IMETHODIMP
 
250
nsInputStreamTransport::IsNonBlocking(PRBool *result)
 
251
{
 
252
    *result = PR_FALSE;
 
253
    return NS_OK;
 
254
}
 
255
 
 
256
//-----------------------------------------------------------------------------
 
257
// nsOutputStreamTransport
 
258
//
 
259
// Implements nsIOutputStream as a wrapper around the real input stream.  This
 
260
// allows the transport to support seeking, range-limiting, progress reporting,
 
261
// and close-when-done semantics while utilizing NS_AsyncCopy.
 
262
//-----------------------------------------------------------------------------
 
263
 
 
264
class nsOutputStreamTransport : public nsITransport
 
265
                              , public nsIOutputStream
 
266
{
 
267
public:
 
268
    NS_DECL_ISUPPORTS
 
269
    NS_DECL_NSITRANSPORT
 
270
    NS_DECL_NSIOUTPUTSTREAM
 
271
 
 
272
    nsOutputStreamTransport(nsIOutputStream *sink,
 
273
                            PRUint32 offset,
 
274
                            PRUint32 limit,
 
275
                            PRBool closeWhenDone)
 
276
        : mSink(sink)
 
277
        , mOffset(offset)
 
278
        , mLimit(limit)
 
279
        , mCloseWhenDone(closeWhenDone)
 
280
        , mFirstTime(PR_TRUE)
 
281
        , mInProgress(PR_FALSE)
 
282
    {
 
283
    }
 
284
 
 
285
    virtual ~nsOutputStreamTransport()
 
286
    {
 
287
    }
 
288
 
 
289
private:
 
290
    nsCOMPtr<nsIAsyncOutputStream>  mPipeOut;
 
291
 
 
292
    // while the copy is active, these members may only be accessed from the
 
293
    // nsIOutputStream implementation.
 
294
    nsCOMPtr<nsITransportEventSink> mEventSink;
 
295
    nsCOMPtr<nsIOutputStream>       mSink;
 
296
    PRUint32                        mOffset;
 
297
    PRUint32                        mLimit;
 
298
    PRPackedBool                    mCloseWhenDone;
 
299
    PRPackedBool                    mFirstTime;
 
300
 
 
301
    // this variable serves as a lock to prevent the state of the transport
 
302
    // from being modified once the copy is in progress.
 
303
    PRPackedBool                    mInProgress;
 
304
};
 
305
 
 
306
NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamTransport,
 
307
                              nsITransport,
 
308
                              nsIOutputStream)
 
309
 
 
310
/** nsITransport **/
 
311
 
 
312
NS_IMETHODIMP
 
313
nsOutputStreamTransport::OpenInputStream(PRUint32 flags,
 
314
                                         PRUint32 segsize,
 
315
                                         PRUint32 segcount,
 
316
                                         nsIInputStream **result)
 
317
{
 
318
    // this transport only supports writing!
 
319
    NS_NOTREACHED("nsOutputStreamTransport::OpenInputStream");
 
320
    return NS_ERROR_UNEXPECTED;
 
321
}
 
322
 
 
323
NS_IMETHODIMP
 
324
nsOutputStreamTransport::OpenOutputStream(PRUint32 flags,
 
325
                                          PRUint32 segsize,
 
326
                                          PRUint32 segcount,
 
327
                                          nsIOutputStream **result)
 
328
{
 
329
    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
 
330
 
 
331
    nsresult rv;
 
332
    nsCOMPtr<nsIEventTarget> target =
 
333
            do_GetService(NS_IOTHREADPOOL_CONTRACTID, &rv);
 
334
    if (NS_FAILED(rv)) return rv;
 
335
 
 
336
    // XXX if the caller requests an unbuffered stream, then perhaps
 
337
    //     we'd want to simply return mSink; however, then we would
 
338
    //     not be writing to mSink on a background thread.  is this ok?
 
339
 
 
340
    PRBool nonblocking = !(flags & OPEN_BLOCKING);
 
341
 
 
342
    net_ResolveSegmentParams(segsize, segcount);
 
343
    nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
 
344
 
 
345
    nsCOMPtr<nsIAsyncInputStream> pipeIn;
 
346
    rv = NS_NewPipe2(getter_AddRefs(pipeIn),
 
347
                     getter_AddRefs(mPipeOut),
 
348
                     PR_TRUE, nonblocking,
 
349
                     segsize, segcount, segalloc);
 
350
    if (NS_FAILED(rv)) return rv;
 
351
 
 
352
    mInProgress = PR_TRUE;
 
353
 
 
354
    // startup async copy process...
 
355
    rv = NS_AsyncCopy(pipeIn, this, target,
 
356
                      NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
 
357
    if (NS_SUCCEEDED(rv))
 
358
        NS_ADDREF(*result = mPipeOut);
 
359
 
 
360
    return rv;
 
361
}
 
362
 
 
363
NS_IMETHODIMP
 
364
nsOutputStreamTransport::Close(nsresult reason)
 
365
{
 
366
    if (NS_SUCCEEDED(reason))
 
367
        reason = NS_BASE_STREAM_CLOSED;
 
368
 
 
369
    return mPipeOut->CloseWithStatus(reason);
 
370
}
 
371
 
 
372
NS_IMETHODIMP
 
373
nsOutputStreamTransport::SetEventSink(nsITransportEventSink *sink,
 
374
                                      nsIEventTarget *target)
 
375
{
 
376
    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
 
377
 
 
378
    if (target)
 
379
        return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
 
380
                                              sink, target);
 
381
 
 
382
    mEventSink = sink;
 
383
    return NS_OK;
 
384
}
 
385
 
 
386
/** nsIOutputStream **/
 
387
 
 
388
NS_IMETHODIMP
 
389
nsOutputStreamTransport::Close()
 
390
{
 
391
    if (mCloseWhenDone)
 
392
        mSink->Close();
 
393
 
 
394
    // make additional writes return early...
 
395
    mOffset = mLimit = 0;
 
396
    return NS_OK;
 
397
}
 
398
 
 
399
NS_IMETHODIMP
 
400
nsOutputStreamTransport::Flush()
 
401
{
 
402
    return NS_OK;
 
403
}
 
404
 
 
405
NS_IMETHODIMP
 
406
nsOutputStreamTransport::Write(const char *buf, PRUint32 count, PRUint32 *result)
 
407
{
 
408
    if (mFirstTime) {
 
409
        mFirstTime = PR_FALSE;
 
410
        if (mOffset) {
 
411
            // write to current position if offset equal to max
 
412
            if (mOffset != PR_UINT32_MAX) {
 
413
                nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSink);
 
414
                if (seekable)
 
415
                    seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
 
416
            }
 
417
            // reset offset to zero so we can use it to enforce limit
 
418
            mOffset = 0;
 
419
        }
 
420
    }
 
421
 
 
422
    // limit amount written
 
423
    PRUint32 max = mLimit - mOffset;
 
424
    if (max == 0) {
 
425
        *result = 0;
 
426
        return NS_OK;
 
427
    }
 
428
        
 
429
    if (count > max)
 
430
        count = max;
 
431
 
 
432
    nsresult rv = mSink->Write(buf, count, result);
 
433
 
 
434
    if (NS_SUCCEEDED(rv)) {
 
435
        mOffset += *result;
 
436
        if (mEventSink)
 
437
            mEventSink->OnTransportStatus(this, STATUS_WRITING, mOffset, mLimit);
 
438
    }
 
439
    return rv;
 
440
}
 
441
 
 
442
NS_IMETHODIMP
 
443
nsOutputStreamTransport::WriteSegments(nsReadSegmentFun reader, void *closure,
 
444
                                       PRUint32 count, PRUint32 *result)
 
445
{
 
446
    return NS_ERROR_NOT_IMPLEMENTED;
 
447
}
 
448
 
 
449
NS_IMETHODIMP
 
450
nsOutputStreamTransport::WriteFrom(nsIInputStream *in, PRUint32 count, PRUint32 *result)
 
451
{
 
452
    return NS_ERROR_NOT_IMPLEMENTED;
 
453
}
 
454
 
 
455
NS_IMETHODIMP
 
456
nsOutputStreamTransport::IsNonBlocking(PRBool *result)
 
457
{
 
458
    *result = PR_FALSE;
 
459
    return NS_OK;
 
460
}
 
461
 
 
462
//-----------------------------------------------------------------------------
 
463
// nsStreamTransportService
 
464
//-----------------------------------------------------------------------------
 
465
 
 
466
NS_IMPL_THREADSAFE_ISUPPORTS1(nsStreamTransportService, nsIStreamTransportService)
 
467
 
 
468
NS_IMETHODIMP
 
469
nsStreamTransportService::CreateInputTransport(nsIInputStream *stream,
 
470
                                               PRInt32 offset,
 
471
                                               PRInt32 limit,
 
472
                                               PRBool closeWhenDone,
 
473
                                               nsITransport **result)
 
474
{
 
475
    nsInputStreamTransport *trans =
 
476
        new nsInputStreamTransport(stream, offset, limit, closeWhenDone);
 
477
    if (!trans)
 
478
        return NS_ERROR_OUT_OF_MEMORY;
 
479
    NS_ADDREF(*result = trans);
 
480
    return NS_OK;
 
481
}
 
482
 
 
483
NS_IMETHODIMP
 
484
nsStreamTransportService::CreateOutputTransport(nsIOutputStream *stream,
 
485
                                                PRInt32 offset,
 
486
                                                PRInt32 limit,
 
487
                                                PRBool closeWhenDone,
 
488
                                                nsITransport **result)
 
489
{
 
490
    nsOutputStreamTransport *trans =
 
491
        new nsOutputStreamTransport(stream, offset, limit, closeWhenDone);
 
492
    if (!trans)
 
493
        return NS_ERROR_OUT_OF_MEMORY;
 
494
    NS_ADDREF(*result = trans);
 
495
    return NS_OK;
 
496
}