Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / base / nsAsyncRedirectVerifyHelper.cpp
blob2863f5d56a1f93bdfb03b89a910074de5dda7130
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include "mozilla/Logging.h"
7 #include "mozilla/SpinEventLoopUntil.h"
8 #include "nsAsyncRedirectVerifyHelper.h"
9 #include "nsThreadUtils.h"
10 #include "nsNetUtil.h"
12 #include "nsIOService.h"
13 #include "nsIChannel.h"
14 #include "nsIHttpChannelInternal.h"
15 #include "nsIAsyncVerifyRedirectCallback.h"
16 #include "nsILoadInfo.h"
18 namespace mozilla {
19 namespace net {
21 static LazyLogModule gRedirectLog("nsRedirect");
22 #undef LOG
23 #define LOG(args) MOZ_LOG(gRedirectLog, LogLevel::Debug, args)
25 NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper, nsIAsyncVerifyRedirectCallback,
26 nsIRunnable, nsINamed)
28 class nsAsyncVerifyRedirectCallbackEvent : public Runnable {
29 public:
30 nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback* cb,
31 nsresult result)
32 : Runnable("nsAsyncVerifyRedirectCallbackEvent"),
33 mCallback(cb),
34 mResult(result) {}
36 NS_IMETHOD Run() override {
37 LOG(
38 ("nsAsyncVerifyRedirectCallbackEvent::Run() "
39 "callback to %p with result %" PRIx32,
40 mCallback.get(), static_cast<uint32_t>(mResult)));
41 (void)mCallback->OnRedirectVerifyCallback(mResult);
42 return NS_OK;
45 private:
46 nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
47 nsresult mResult;
50 nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper() {
51 NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
52 "Did not receive all required callbacks!");
55 nsresult nsAsyncRedirectVerifyHelper::Init(
56 nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags,
57 nsIEventTarget* mainThreadEventTarget, bool synchronize) {
58 LOG(
59 ("nsAsyncRedirectVerifyHelper::Init() "
60 "oldChan=%p newChan=%p",
61 oldChan, newChan));
62 mOldChan = oldChan;
63 mNewChan = newChan;
64 mFlags = flags;
65 mCallbackEventTarget = NS_IsMainThread() && mainThreadEventTarget
66 ? mainThreadEventTarget
67 : GetCurrentSerialEventTarget();
69 if (!(flags & (nsIChannelEventSink::REDIRECT_INTERNAL |
70 nsIChannelEventSink::REDIRECT_STS_UPGRADE |
71 nsIChannelEventSink::REDIRECT_TRANSPARENT))) {
72 nsCOMPtr<nsILoadInfo> loadInfo = oldChan->LoadInfo();
73 if (loadInfo->GetDontFollowRedirects()) {
74 ExplicitCallback(NS_BINDING_ABORTED);
75 return NS_OK;
79 if (synchronize) mWaitingForRedirectCallback = true;
81 nsCOMPtr<nsIRunnable> runnable = this;
82 nsresult rv;
83 rv = mainThreadEventTarget
84 ? mainThreadEventTarget->Dispatch(runnable.forget())
85 : GetMainThreadSerialEventTarget()->Dispatch(runnable.forget());
86 NS_ENSURE_SUCCESS(rv, rv);
88 if (synchronize) {
89 if (!SpinEventLoopUntil("nsAsyncRedirectVerifyHelper::Init"_ns,
90 [&]() { return !mWaitingForRedirectCallback; })) {
91 return NS_ERROR_UNEXPECTED;
95 return NS_OK;
98 NS_IMETHODIMP
99 nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result) {
100 LOG(
101 ("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
102 "result=%" PRIx32 " expectedCBs=%u mResult=%" PRIx32,
103 static_cast<uint32_t>(result), mExpectedCallbacks,
104 static_cast<uint32_t>(mResult)));
106 MOZ_DIAGNOSTIC_ASSERT(
107 mExpectedCallbacks > 0,
108 "OnRedirectVerifyCallback called more times than expected");
109 if (mExpectedCallbacks <= 0) {
110 return NS_ERROR_UNEXPECTED;
113 --mExpectedCallbacks;
115 // If response indicates failure we may call back immediately
116 if (NS_FAILED(result)) {
117 // We chose to store the first failure-value (as opposed to the last)
118 if (NS_SUCCEEDED(mResult)) mResult = result;
120 // If InitCallback() has been called, just invoke the callback and
121 // return. Otherwise it will be invoked from InitCallback()
122 if (mCallbackInitiated) {
123 ExplicitCallback(mResult);
124 return NS_OK;
128 // If the expected-counter is in balance and InitCallback() was called, all
129 // sinks have agreed that the redirect is ok and we can invoke our callback
130 if (mCallbackInitiated && mExpectedCallbacks == 0) {
131 ExplicitCallback(mResult);
134 return NS_OK;
137 nsresult nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(
138 nsIChannelEventSink* sink, nsIChannel* oldChannel, nsIChannel* newChannel,
139 uint32_t flags) {
140 LOG(
141 ("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
142 "sink=%p expectedCBs=%u mResult=%" PRIx32,
143 sink, mExpectedCallbacks, static_cast<uint32_t>(mResult)));
145 ++mExpectedCallbacks;
147 if (IsOldChannelCanceled()) {
148 LOG(
149 (" old channel has been canceled, cancel the redirect by "
150 "emulating OnRedirectVerifyCallback..."));
151 (void)OnRedirectVerifyCallback(NS_BINDING_ABORTED);
152 return NS_BINDING_ABORTED;
155 nsresult rv =
156 sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
158 LOG((" result=%" PRIx32 " expectedCBs=%u", static_cast<uint32_t>(rv),
159 mExpectedCallbacks));
161 // If the sink returns failure from this call the redirect is vetoed. We
162 // emulate a callback from the sink in this case in order to perform all
163 // the necessary logic.
164 if (NS_FAILED(rv)) {
165 LOG((" emulating OnRedirectVerifyCallback..."));
166 (void)OnRedirectVerifyCallback(rv);
169 return rv; // Return the actual status since our caller may need it
172 void nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result) {
173 LOG(
174 ("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
175 "result=%" PRIx32
176 " expectedCBs=%u mCallbackInitiated=%u mResult=%" PRIx32,
177 static_cast<uint32_t>(result), mExpectedCallbacks, mCallbackInitiated,
178 static_cast<uint32_t>(mResult)));
180 nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(
181 do_QueryInterface(mOldChan));
183 if (!callback || !mCallbackEventTarget) {
184 LOG(
185 ("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
186 "callback=%p mCallbackEventTarget=%p",
187 callback.get(), mCallbackEventTarget.get()));
188 return;
191 mCallbackInitiated = false; // reset to ensure only one callback
192 mWaitingForRedirectCallback = false;
194 // Now, dispatch the callback on the event-target which called Init()
195 nsCOMPtr<nsIRunnable> event =
196 new nsAsyncVerifyRedirectCallbackEvent(callback, result);
197 if (!event) {
198 NS_WARNING(
199 "nsAsyncRedirectVerifyHelper::ExplicitCallback() "
200 "failed creating callback event!");
201 return;
203 nsresult rv = mCallbackEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
204 if (NS_FAILED(rv)) {
205 NS_WARNING(
206 "nsAsyncRedirectVerifyHelper::ExplicitCallback() "
207 "failed dispatching callback event!");
208 } else {
209 LOG(
210 ("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
211 "dispatched callback event=%p",
212 event.get()));
216 void nsAsyncRedirectVerifyHelper::InitCallback() {
217 LOG(
218 ("nsAsyncRedirectVerifyHelper::InitCallback() "
219 "expectedCBs=%d mResult=%" PRIx32,
220 mExpectedCallbacks, static_cast<uint32_t>(mResult)));
222 mCallbackInitiated = true;
224 // Invoke the callback if we are done
225 if (mExpectedCallbacks == 0) ExplicitCallback(mResult);
228 NS_IMETHODIMP
229 nsAsyncRedirectVerifyHelper::GetName(nsACString& aName) {
230 aName.AssignLiteral("nsAsyncRedirectVerifyHelper");
231 return NS_OK;
234 NS_IMETHODIMP
235 nsAsyncRedirectVerifyHelper::Run() {
236 /* If the channel got canceled after it fired AsyncOnChannelRedirect
237 * and before we got here, mostly because docloader load has been canceled,
238 * we must completely ignore this notification and prevent any further
239 * notification.
241 if (IsOldChannelCanceled()) {
242 ExplicitCallback(NS_BINDING_ABORTED);
243 return NS_OK;
246 // If transparent, avoid notifying the observers.
247 if (mFlags & nsIChannelEventSink::REDIRECT_TRANSPARENT) {
248 ExplicitCallback(NS_OK);
249 return NS_OK;
252 // First, the global observer
253 NS_ASSERTION(gIOService, "Must have an IO service at this point");
254 LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
255 nsresult rv =
256 gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan, mFlags, this);
257 if (NS_FAILED(rv)) {
258 ExplicitCallback(rv);
259 return NS_OK;
262 // Now, the per-channel observers
263 nsCOMPtr<nsIChannelEventSink> sink;
264 NS_QueryNotificationCallbacks(mOldChan, sink);
265 if (sink) {
266 LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
267 rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
270 // All invocations to AsyncOnChannelRedirect has been done - call
271 // InitCallback() to flag this
272 InitCallback();
273 return NS_OK;
276 bool nsAsyncRedirectVerifyHelper::IsOldChannelCanceled() {
277 if (!mOldChan) {
278 return false;
280 bool canceled;
281 nsresult rv = mOldChan->GetCanceled(&canceled);
282 return NS_SUCCEEDED(rv) && canceled;
285 } // namespace net
286 } // namespace mozilla