Source code

Revision control

Other Tools

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