1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,270 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifdef MOZ_LOGGING 1.10 +#define FORCE_PR_LOG 1.11 +#endif 1.12 + 1.13 +#include "prlog.h" 1.14 +#include "nsAsyncRedirectVerifyHelper.h" 1.15 +#include "nsThreadUtils.h" 1.16 +#include "nsNetUtil.h" 1.17 + 1.18 +#include "nsIOService.h" 1.19 +#include "nsIChannel.h" 1.20 +#include "nsIHttpChannelInternal.h" 1.21 +#include "nsIAsyncVerifyRedirectCallback.h" 1.22 + 1.23 +#undef LOG 1.24 +#ifdef PR_LOGGING 1.25 +static PRLogModuleInfo * 1.26 +GetRedirectLog() 1.27 +{ 1.28 + static PRLogModuleInfo *sLog; 1.29 + if (!sLog) 1.30 + sLog = PR_NewLogModule("nsRedirect"); 1.31 + return sLog; 1.32 +} 1.33 +#define LOG(args) PR_LOG(GetRedirectLog(), PR_LOG_DEBUG, args) 1.34 +#else 1.35 +#define LOG(args) 1.36 +#endif 1.37 + 1.38 +NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper, 1.39 + nsIAsyncVerifyRedirectCallback, 1.40 + nsIRunnable) 1.41 + 1.42 +class nsAsyncVerifyRedirectCallbackEvent : public nsRunnable { 1.43 +public: 1.44 + nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb, 1.45 + nsresult result) 1.46 + : mCallback(cb), mResult(result) { 1.47 + } 1.48 + 1.49 + NS_IMETHOD Run() 1.50 + { 1.51 + LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() " 1.52 + "callback to %p with result %x", 1.53 + mCallback.get(), mResult)); 1.54 + (void) mCallback->OnRedirectVerifyCallback(mResult); 1.55 + return NS_OK; 1.56 + } 1.57 +private: 1.58 + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback; 1.59 + nsresult mResult; 1.60 +}; 1.61 + 1.62 +nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper() 1.63 + : mCallbackInitiated(false), 1.64 + mExpectedCallbacks(0), 1.65 + mResult(NS_OK) 1.66 +{ 1.67 +} 1.68 + 1.69 +nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper() 1.70 +{ 1.71 + NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0, 1.72 + "Did not receive all required callbacks!"); 1.73 +} 1.74 + 1.75 +nsresult 1.76 +nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan, 1.77 + uint32_t flags, bool synchronize) 1.78 +{ 1.79 + LOG(("nsAsyncRedirectVerifyHelper::Init() " 1.80 + "oldChan=%p newChan=%p", oldChan, newChan)); 1.81 + mOldChan = oldChan; 1.82 + mNewChan = newChan; 1.83 + mFlags = flags; 1.84 + mCallbackThread = do_GetCurrentThread(); 1.85 + 1.86 + if (synchronize) 1.87 + mWaitingForRedirectCallback = true; 1.88 + 1.89 + nsresult rv; 1.90 + rv = NS_DispatchToMainThread(this); 1.91 + NS_ENSURE_SUCCESS(rv, rv); 1.92 + 1.93 + if (synchronize) { 1.94 + nsIThread *thread = NS_GetCurrentThread(); 1.95 + while (mWaitingForRedirectCallback) { 1.96 + if (!NS_ProcessNextEvent(thread)) { 1.97 + return NS_ERROR_UNEXPECTED; 1.98 + } 1.99 + } 1.100 + } 1.101 + 1.102 + return NS_OK; 1.103 +} 1.104 + 1.105 +NS_IMETHODIMP 1.106 +nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result) 1.107 +{ 1.108 + LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() " 1.109 + "result=%x expectedCBs=%u mResult=%x", 1.110 + result, mExpectedCallbacks, mResult)); 1.111 + 1.112 + --mExpectedCallbacks; 1.113 + 1.114 + // If response indicates failure we may call back immediately 1.115 + if (NS_FAILED(result)) { 1.116 + // We chose to store the first failure-value (as opposed to the last) 1.117 + if (NS_SUCCEEDED(mResult)) 1.118 + mResult = result; 1.119 + 1.120 + // If InitCallback() has been called, just invoke the callback and 1.121 + // return. Otherwise it will be invoked from InitCallback() 1.122 + if (mCallbackInitiated) { 1.123 + ExplicitCallback(mResult); 1.124 + return NS_OK; 1.125 + } 1.126 + } 1.127 + 1.128 + // If the expected-counter is in balance and InitCallback() was called, all 1.129 + // sinks have agreed that the redirect is ok and we can invoke our callback 1.130 + if (mCallbackInitiated && mExpectedCallbacks == 0) { 1.131 + ExplicitCallback(mResult); 1.132 + } 1.133 + 1.134 + return NS_OK; 1.135 +} 1.136 + 1.137 +nsresult 1.138 +nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink, 1.139 + nsIChannel *oldChannel, 1.140 + nsIChannel *newChannel, 1.141 + uint32_t flags) 1.142 +{ 1.143 + LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() " 1.144 + "sink=%p expectedCBs=%u mResult=%x", 1.145 + sink, mExpectedCallbacks, mResult)); 1.146 + 1.147 + ++mExpectedCallbacks; 1.148 + 1.149 + if (IsOldChannelCanceled()) { 1.150 + LOG((" old channel has been canceled, cancel the redirect by " 1.151 + "emulating OnRedirectVerifyCallback...")); 1.152 + (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED); 1.153 + return NS_BINDING_ABORTED; 1.154 + } 1.155 + 1.156 + nsresult rv = 1.157 + sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this); 1.158 + 1.159 + LOG((" result=%x expectedCBs=%u", rv, mExpectedCallbacks)); 1.160 + 1.161 + // If the sink returns failure from this call the redirect is vetoed. We 1.162 + // emulate a callback from the sink in this case in order to perform all 1.163 + // the necessary logic. 1.164 + if (NS_FAILED(rv)) { 1.165 + LOG((" emulating OnRedirectVerifyCallback...")); 1.166 + (void) OnRedirectVerifyCallback(rv); 1.167 + } 1.168 + 1.169 + return rv; // Return the actual status since our caller may need it 1.170 +} 1.171 + 1.172 +void 1.173 +nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result) 1.174 +{ 1.175 + LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() " 1.176 + "result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x", 1.177 + result, mExpectedCallbacks, mCallbackInitiated, mResult)); 1.178 + 1.179 + nsCOMPtr<nsIAsyncVerifyRedirectCallback> 1.180 + callback(do_QueryInterface(mOldChan)); 1.181 + 1.182 + if (!callback || !mCallbackThread) { 1.183 + LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() " 1.184 + "callback=%p mCallbackThread=%p", callback.get(), mCallbackThread.get())); 1.185 + return; 1.186 + } 1.187 + 1.188 + mCallbackInitiated = false; // reset to ensure only one callback 1.189 + mWaitingForRedirectCallback = false; 1.190 + 1.191 + // Now, dispatch the callback on the event-target which called Init() 1.192 + nsRefPtr<nsIRunnable> event = 1.193 + new nsAsyncVerifyRedirectCallbackEvent(callback, result); 1.194 + if (!event) { 1.195 + NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() " 1.196 + "failed creating callback event!"); 1.197 + return; 1.198 + } 1.199 + nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL); 1.200 + if (NS_FAILED(rv)) { 1.201 + NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() " 1.202 + "failed dispatching callback event!"); 1.203 + } else { 1.204 + LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() " 1.205 + "dispatched callback event=%p", event.get())); 1.206 + } 1.207 + 1.208 +} 1.209 + 1.210 +void 1.211 +nsAsyncRedirectVerifyHelper::InitCallback() 1.212 +{ 1.213 + LOG(("nsAsyncRedirectVerifyHelper::InitCallback() " 1.214 + "expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult)); 1.215 + 1.216 + mCallbackInitiated = true; 1.217 + 1.218 + // Invoke the callback if we are done 1.219 + if (mExpectedCallbacks == 0) 1.220 + ExplicitCallback(mResult); 1.221 +} 1.222 + 1.223 +NS_IMETHODIMP 1.224 +nsAsyncRedirectVerifyHelper::Run() 1.225 +{ 1.226 + /* If the channel got canceled after it fired AsyncOnChannelRedirect 1.227 + * and before we got here, mostly because docloader load has been canceled, 1.228 + * we must completely ignore this notification and prevent any further 1.229 + * notification. 1.230 + */ 1.231 + if (IsOldChannelCanceled()) { 1.232 + ExplicitCallback(NS_BINDING_ABORTED); 1.233 + return NS_OK; 1.234 + } 1.235 + 1.236 + // First, the global observer 1.237 + NS_ASSERTION(gIOService, "Must have an IO service at this point"); 1.238 + LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService...")); 1.239 + nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan, 1.240 + mFlags, this); 1.241 + if (NS_FAILED(rv)) { 1.242 + ExplicitCallback(rv); 1.243 + return NS_OK; 1.244 + } 1.245 + 1.246 + // Now, the per-channel observers 1.247 + nsCOMPtr<nsIChannelEventSink> sink; 1.248 + NS_QueryNotificationCallbacks(mOldChan, sink); 1.249 + if (sink) { 1.250 + LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink...")); 1.251 + rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags); 1.252 + } 1.253 + 1.254 + // All invocations to AsyncOnChannelRedirect has been done - call 1.255 + // InitCallback() to flag this 1.256 + InitCallback(); 1.257 + return NS_OK; 1.258 +} 1.259 + 1.260 +bool 1.261 +nsAsyncRedirectVerifyHelper::IsOldChannelCanceled() 1.262 +{ 1.263 + bool canceled; 1.264 + nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal = 1.265 + do_QueryInterface(mOldChan); 1.266 + if (oldChannelInternal) { 1.267 + oldChannelInternal->GetCanceled(&canceled); 1.268 + if (canceled) 1.269 + return true; 1.270 + } 1.271 + 1.272 + return false; 1.273 +}