netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial