netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:c138bdc056a2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #ifdef MOZ_LOGGING
7 #define FORCE_PR_LOG
8 #endif
9
10 #include "prlog.h"
11 #include "nsAsyncRedirectVerifyHelper.h"
12 #include "nsThreadUtils.h"
13 #include "nsNetUtil.h"
14
15 #include "nsIOService.h"
16 #include "nsIChannel.h"
17 #include "nsIHttpChannelInternal.h"
18 #include "nsIAsyncVerifyRedirectCallback.h"
19
20 #undef LOG
21 #ifdef PR_LOGGING
22 static PRLogModuleInfo *
23 GetRedirectLog()
24 {
25 static PRLogModuleInfo *sLog;
26 if (!sLog)
27 sLog = PR_NewLogModule("nsRedirect");
28 return sLog;
29 }
30 #define LOG(args) PR_LOG(GetRedirectLog(), PR_LOG_DEBUG, args)
31 #else
32 #define LOG(args)
33 #endif
34
35 NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper,
36 nsIAsyncVerifyRedirectCallback,
37 nsIRunnable)
38
39 class nsAsyncVerifyRedirectCallbackEvent : public nsRunnable {
40 public:
41 nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
42 nsresult result)
43 : mCallback(cb), mResult(result) {
44 }
45
46 NS_IMETHOD Run()
47 {
48 LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() "
49 "callback to %p with result %x",
50 mCallback.get(), mResult));
51 (void) mCallback->OnRedirectVerifyCallback(mResult);
52 return NS_OK;
53 }
54 private:
55 nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
56 nsresult mResult;
57 };
58
59 nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper()
60 : mCallbackInitiated(false),
61 mExpectedCallbacks(0),
62 mResult(NS_OK)
63 {
64 }
65
66 nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper()
67 {
68 NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
69 "Did not receive all required callbacks!");
70 }
71
72 nsresult
73 nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
74 uint32_t flags, bool synchronize)
75 {
76 LOG(("nsAsyncRedirectVerifyHelper::Init() "
77 "oldChan=%p newChan=%p", oldChan, newChan));
78 mOldChan = oldChan;
79 mNewChan = newChan;
80 mFlags = flags;
81 mCallbackThread = do_GetCurrentThread();
82
83 if (synchronize)
84 mWaitingForRedirectCallback = true;
85
86 nsresult rv;
87 rv = NS_DispatchToMainThread(this);
88 NS_ENSURE_SUCCESS(rv, rv);
89
90 if (synchronize) {
91 nsIThread *thread = NS_GetCurrentThread();
92 while (mWaitingForRedirectCallback) {
93 if (!NS_ProcessNextEvent(thread)) {
94 return NS_ERROR_UNEXPECTED;
95 }
96 }
97 }
98
99 return NS_OK;
100 }
101
102 NS_IMETHODIMP
103 nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result)
104 {
105 LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
106 "result=%x expectedCBs=%u mResult=%x",
107 result, mExpectedCallbacks, mResult));
108
109 --mExpectedCallbacks;
110
111 // If response indicates failure we may call back immediately
112 if (NS_FAILED(result)) {
113 // We chose to store the first failure-value (as opposed to the last)
114 if (NS_SUCCEEDED(mResult))
115 mResult = result;
116
117 // If InitCallback() has been called, just invoke the callback and
118 // return. Otherwise it will be invoked from InitCallback()
119 if (mCallbackInitiated) {
120 ExplicitCallback(mResult);
121 return NS_OK;
122 }
123 }
124
125 // If the expected-counter is in balance and InitCallback() was called, all
126 // sinks have agreed that the redirect is ok and we can invoke our callback
127 if (mCallbackInitiated && mExpectedCallbacks == 0) {
128 ExplicitCallback(mResult);
129 }
130
131 return NS_OK;
132 }
133
134 nsresult
135 nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink,
136 nsIChannel *oldChannel,
137 nsIChannel *newChannel,
138 uint32_t flags)
139 {
140 LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
141 "sink=%p expectedCBs=%u mResult=%x",
142 sink, mExpectedCallbacks, mResult));
143
144 ++mExpectedCallbacks;
145
146 if (IsOldChannelCanceled()) {
147 LOG((" old channel has been canceled, cancel the redirect by "
148 "emulating OnRedirectVerifyCallback..."));
149 (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED);
150 return NS_BINDING_ABORTED;
151 }
152
153 nsresult rv =
154 sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
155
156 LOG((" result=%x expectedCBs=%u", rv, mExpectedCallbacks));
157
158 // If the sink returns failure from this call the redirect is vetoed. We
159 // emulate a callback from the sink in this case in order to perform all
160 // the necessary logic.
161 if (NS_FAILED(rv)) {
162 LOG((" emulating OnRedirectVerifyCallback..."));
163 (void) OnRedirectVerifyCallback(rv);
164 }
165
166 return rv; // Return the actual status since our caller may need it
167 }
168
169 void
170 nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result)
171 {
172 LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
173 "result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x",
174 result, mExpectedCallbacks, mCallbackInitiated, mResult));
175
176 nsCOMPtr<nsIAsyncVerifyRedirectCallback>
177 callback(do_QueryInterface(mOldChan));
178
179 if (!callback || !mCallbackThread) {
180 LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
181 "callback=%p mCallbackThread=%p", callback.get(), mCallbackThread.get()));
182 return;
183 }
184
185 mCallbackInitiated = false; // reset to ensure only one callback
186 mWaitingForRedirectCallback = false;
187
188 // Now, dispatch the callback on the event-target which called Init()
189 nsRefPtr<nsIRunnable> event =
190 new nsAsyncVerifyRedirectCallbackEvent(callback, result);
191 if (!event) {
192 NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
193 "failed creating callback event!");
194 return;
195 }
196 nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL);
197 if (NS_FAILED(rv)) {
198 NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
199 "failed dispatching callback event!");
200 } else {
201 LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
202 "dispatched callback event=%p", event.get()));
203 }
204
205 }
206
207 void
208 nsAsyncRedirectVerifyHelper::InitCallback()
209 {
210 LOG(("nsAsyncRedirectVerifyHelper::InitCallback() "
211 "expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult));
212
213 mCallbackInitiated = true;
214
215 // Invoke the callback if we are done
216 if (mExpectedCallbacks == 0)
217 ExplicitCallback(mResult);
218 }
219
220 NS_IMETHODIMP
221 nsAsyncRedirectVerifyHelper::Run()
222 {
223 /* If the channel got canceled after it fired AsyncOnChannelRedirect
224 * and before we got here, mostly because docloader load has been canceled,
225 * we must completely ignore this notification and prevent any further
226 * notification.
227 */
228 if (IsOldChannelCanceled()) {
229 ExplicitCallback(NS_BINDING_ABORTED);
230 return NS_OK;
231 }
232
233 // First, the global observer
234 NS_ASSERTION(gIOService, "Must have an IO service at this point");
235 LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
236 nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan,
237 mFlags, this);
238 if (NS_FAILED(rv)) {
239 ExplicitCallback(rv);
240 return NS_OK;
241 }
242
243 // Now, the per-channel observers
244 nsCOMPtr<nsIChannelEventSink> sink;
245 NS_QueryNotificationCallbacks(mOldChan, sink);
246 if (sink) {
247 LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
248 rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
249 }
250
251 // All invocations to AsyncOnChannelRedirect has been done - call
252 // InitCallback() to flag this
253 InitCallback();
254 return NS_OK;
255 }
256
257 bool
258 nsAsyncRedirectVerifyHelper::IsOldChannelCanceled()
259 {
260 bool canceled;
261 nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
262 do_QueryInterface(mOldChan);
263 if (oldChannelInternal) {
264 oldChannelInternal->GetCanceled(&canceled);
265 if (canceled)
266 return true;
267 }
268
269 return false;
270 }

mercurial