1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsPACMan.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,754 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsPACMan.h" 1.11 +#include "nsThreadUtils.h" 1.12 +#include "nsIAuthPrompt.h" 1.13 +#include "nsIPromptFactory.h" 1.14 +#include "nsIHttpChannel.h" 1.15 +#include "nsIPrefService.h" 1.16 +#include "nsIPrefBranch.h" 1.17 +#include "nsNetUtil.h" 1.18 +#include "nsIAsyncVerifyRedirectCallback.h" 1.19 +#include "nsISystemProxySettings.h" 1.20 +#ifdef MOZ_NUWA_PROCESS 1.21 +#include "ipc/Nuwa.h" 1.22 +#endif 1.23 + 1.24 +//----------------------------------------------------------------------------- 1.25 +using namespace mozilla; 1.26 +using namespace mozilla::net; 1.27 + 1.28 +#if defined(PR_LOGGING) 1.29 +#endif 1.30 +#undef LOG 1.31 +#define LOG(args) PR_LOG(GetProxyLog(), PR_LOG_DEBUG, args) 1.32 + 1.33 +// The PAC thread does evaluations of both PAC files and 1.34 +// nsISystemProxySettings because they can both block the calling thread and we 1.35 +// don't want that on the main thread 1.36 + 1.37 +// Check to see if the underlying request was not an error page in the case of 1.38 +// a HTTP request. For other types of channels, just return true. 1.39 +static bool 1.40 +HttpRequestSucceeded(nsIStreamLoader *loader) 1.41 +{ 1.42 + nsCOMPtr<nsIRequest> request; 1.43 + loader->GetRequest(getter_AddRefs(request)); 1.44 + 1.45 + bool result = true; // default to assuming success 1.46 + 1.47 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request); 1.48 + if (httpChannel) 1.49 + httpChannel->GetRequestSucceeded(&result); 1.50 + 1.51 + return result; 1.52 +} 1.53 + 1.54 +//----------------------------------------------------------------------------- 1.55 + 1.56 +// The ExecuteCallback runnable is triggered by 1.57 +// nsPACManCallback::OnQueryComplete on the Main thread when its completion is 1.58 +// discovered on the pac thread 1.59 + 1.60 +class ExecuteCallback MOZ_FINAL : public nsRunnable 1.61 +{ 1.62 +public: 1.63 + ExecuteCallback(nsPACManCallback *aCallback, 1.64 + nsresult status) 1.65 + : mCallback(aCallback) 1.66 + , mStatus(status) 1.67 + { 1.68 + } 1.69 + 1.70 + void SetPACString(const nsCString &pacString) 1.71 + { 1.72 + mPACString = pacString; 1.73 + } 1.74 + 1.75 + void SetPACURL(const nsCString &pacURL) 1.76 + { 1.77 + mPACURL = pacURL; 1.78 + } 1.79 + 1.80 + NS_IMETHODIMP Run() 1.81 + { 1.82 + mCallback->OnQueryComplete(mStatus, mPACString, mPACURL); 1.83 + mCallback = nullptr; 1.84 + return NS_OK; 1.85 + } 1.86 + 1.87 +private: 1.88 + nsRefPtr<nsPACManCallback> mCallback; 1.89 + nsresult mStatus; 1.90 + nsCString mPACString; 1.91 + nsCString mPACURL; 1.92 +}; 1.93 + 1.94 +//----------------------------------------------------------------------------- 1.95 + 1.96 +// The PAC thread must be deleted from the main thread, this class 1.97 +// acts as a proxy to do that, as the PACMan is reference counted 1.98 +// and might be destroyed on either thread 1.99 + 1.100 +class ShutdownThread MOZ_FINAL : public nsRunnable 1.101 +{ 1.102 +public: 1.103 + ShutdownThread(nsIThread *thread) 1.104 + : mThread(thread) 1.105 + { 1.106 + } 1.107 + 1.108 + NS_IMETHODIMP Run() 1.109 + { 1.110 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.111 + mThread->Shutdown(); 1.112 + return NS_OK; 1.113 + } 1.114 + 1.115 +private: 1.116 + nsCOMPtr<nsIThread> mThread; 1.117 +}; 1.118 + 1.119 +// Dispatch this to wait until the PAC thread shuts down. 1.120 + 1.121 +class WaitForThreadShutdown MOZ_FINAL : public nsRunnable 1.122 +{ 1.123 +public: 1.124 + explicit WaitForThreadShutdown(nsPACMan *aPACMan) 1.125 + : mPACMan(aPACMan) 1.126 + { 1.127 + } 1.128 + 1.129 + NS_IMETHODIMP Run() 1.130 + { 1.131 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.132 + if (mPACMan->mPACThread) { 1.133 + mPACMan->mPACThread->Shutdown(); 1.134 + mPACMan->mPACThread = nullptr; 1.135 + } 1.136 + return NS_OK; 1.137 + } 1.138 + 1.139 +private: 1.140 + nsRefPtr<nsPACMan> mPACMan; 1.141 +}; 1.142 + 1.143 +//----------------------------------------------------------------------------- 1.144 + 1.145 +// PACLoadComplete allows the PAC thread to tell the main thread that 1.146 +// the javascript PAC file has been installed (perhaps unsuccessfully) 1.147 +// and that there is no reason to queue executions anymore 1.148 + 1.149 +class PACLoadComplete MOZ_FINAL : public nsRunnable 1.150 +{ 1.151 +public: 1.152 + PACLoadComplete(nsPACMan *aPACMan) 1.153 + : mPACMan(aPACMan) 1.154 + { 1.155 + } 1.156 + 1.157 + NS_IMETHODIMP Run() 1.158 + { 1.159 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.160 + mPACMan->mLoader = nullptr; 1.161 + mPACMan->PostProcessPendingQ(); 1.162 + return NS_OK; 1.163 + } 1.164 + 1.165 +private: 1.166 + nsRefPtr<nsPACMan> mPACMan; 1.167 +}; 1.168 + 1.169 +//----------------------------------------------------------------------------- 1.170 + 1.171 +// ExecutePACThreadAction is used to proxy actions from the main 1.172 +// thread onto the PAC thread. There are 3 options: process the queue, 1.173 +// cancel the queue, and setup the javascript context with a new PAC file 1.174 + 1.175 +class ExecutePACThreadAction MOZ_FINAL : public nsRunnable 1.176 +{ 1.177 +public: 1.178 + // by default we just process the queue 1.179 + ExecutePACThreadAction(nsPACMan *aPACMan) 1.180 + : mPACMan(aPACMan) 1.181 + , mCancel(false) 1.182 + , mSetupPAC(false) 1.183 + { } 1.184 + 1.185 + void CancelQueue (nsresult status) 1.186 + { 1.187 + mCancel = true; 1.188 + mCancelStatus = status; 1.189 + } 1.190 + 1.191 + void SetupPAC (const char *text, uint32_t datalen, nsCString &pacURI) 1.192 + { 1.193 + mSetupPAC = true; 1.194 + mSetupPACData.Assign(text, datalen); 1.195 + mSetupPACURI = pacURI; 1.196 + } 1.197 + 1.198 + NS_IMETHODIMP Run() 1.199 + { 1.200 + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); 1.201 + if (mCancel) { 1.202 + mPACMan->CancelPendingQ(mCancelStatus); 1.203 + mCancel = false; 1.204 + return NS_OK; 1.205 + } 1.206 + 1.207 + if (mSetupPAC) { 1.208 + mSetupPAC = false; 1.209 + 1.210 + mPACMan->mPAC.Init(mSetupPACURI, 1.211 + mSetupPACData); 1.212 + 1.213 + nsRefPtr<PACLoadComplete> runnable = new PACLoadComplete(mPACMan); 1.214 + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 1.215 + return NS_OK; 1.216 + } 1.217 + 1.218 + mPACMan->ProcessPendingQ(); 1.219 + return NS_OK; 1.220 + } 1.221 + 1.222 +private: 1.223 + nsRefPtr<nsPACMan> mPACMan; 1.224 + 1.225 + bool mCancel; 1.226 + nsresult mCancelStatus; 1.227 + 1.228 + bool mSetupPAC; 1.229 + nsCString mSetupPACData; 1.230 + nsCString mSetupPACURI; 1.231 +}; 1.232 + 1.233 +//----------------------------------------------------------------------------- 1.234 + 1.235 +PendingPACQuery::PendingPACQuery(nsPACMan *pacMan, nsIURI *uri, 1.236 + nsPACManCallback *callback, 1.237 + bool mainThreadResponse) 1.238 + : mPACMan(pacMan) 1.239 + , mCallback(callback) 1.240 + , mOnMainThreadOnly(mainThreadResponse) 1.241 +{ 1.242 + uri->GetAsciiSpec(mSpec); 1.243 + uri->GetAsciiHost(mHost); 1.244 + uri->GetScheme(mScheme); 1.245 + uri->GetPort(&mPort); 1.246 +} 1.247 + 1.248 +void 1.249 +PendingPACQuery::Complete(nsresult status, const nsCString &pacString) 1.250 +{ 1.251 + if (!mCallback) 1.252 + return; 1.253 + nsRefPtr<ExecuteCallback> runnable = new ExecuteCallback(mCallback, status); 1.254 + runnable->SetPACString(pacString); 1.255 + if (mOnMainThreadOnly) 1.256 + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 1.257 + else 1.258 + runnable->Run(); 1.259 +} 1.260 + 1.261 +void 1.262 +PendingPACQuery::UseAlternatePACFile(const nsCString &pacURL) 1.263 +{ 1.264 + if (!mCallback) 1.265 + return; 1.266 + 1.267 + nsRefPtr<ExecuteCallback> runnable = new ExecuteCallback(mCallback, NS_OK); 1.268 + runnable->SetPACURL(pacURL); 1.269 + if (mOnMainThreadOnly) 1.270 + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 1.271 + else 1.272 + runnable->Run(); 1.273 +} 1.274 + 1.275 +NS_IMETHODIMP 1.276 +PendingPACQuery::Run() 1.277 +{ 1.278 + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); 1.279 + mPACMan->PostQuery(this); 1.280 + return NS_OK; 1.281 +} 1.282 + 1.283 +//----------------------------------------------------------------------------- 1.284 + 1.285 +nsPACMan::nsPACMan() 1.286 + : mLoadPending(false) 1.287 + , mShutdown(false) 1.288 + , mLoadFailureCount(0) 1.289 + , mInProgress(false) 1.290 +{ 1.291 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be created on main thread"); 1.292 +} 1.293 + 1.294 +nsPACMan::~nsPACMan() 1.295 +{ 1.296 + if (mPACThread) { 1.297 + if (NS_IsMainThread()) { 1.298 + mPACThread->Shutdown(); 1.299 + } 1.300 + else { 1.301 + nsRefPtr<ShutdownThread> runnable = new ShutdownThread(mPACThread); 1.302 + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 1.303 + } 1.304 + } 1.305 + 1.306 + NS_ASSERTION(mLoader == nullptr, "pac man not shutdown properly"); 1.307 + NS_ASSERTION(mPendingQ.isEmpty(), "pac man not shutdown properly"); 1.308 +} 1.309 + 1.310 +void 1.311 +nsPACMan::Shutdown() 1.312 +{ 1.313 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be shutdown on main thread"); 1.314 + if (mShutdown) { 1.315 + return; 1.316 + } 1.317 + mShutdown = true; 1.318 + CancelExistingLoad(); 1.319 + PostCancelPendingQ(NS_ERROR_ABORT); 1.320 + 1.321 + nsRefPtr<WaitForThreadShutdown> runnable = new WaitForThreadShutdown(this); 1.322 + NS_DispatchToMainThread(runnable); 1.323 +} 1.324 + 1.325 +nsresult 1.326 +nsPACMan::AsyncGetProxyForChannel(nsIChannel *channel, nsPACManCallback *callback, 1.327 + bool mainThreadResponse) 1.328 +{ 1.329 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.330 + if (mShutdown) 1.331 + return NS_ERROR_NOT_AVAILABLE; 1.332 + 1.333 + // Maybe Reload PAC 1.334 + if (!mPACURISpec.IsEmpty() && !mScheduledReload.IsNull() && 1.335 + TimeStamp::Now() > mScheduledReload) 1.336 + LoadPACFromURI(EmptyCString()); 1.337 + 1.338 + nsCOMPtr<nsIURI> uri; 1.339 + nsresult rv = channel->GetURI(getter_AddRefs(uri)); 1.340 + if (NS_FAILED(rv)) 1.341 + return rv; 1.342 + 1.343 + nsRefPtr<PendingPACQuery> query = 1.344 + new PendingPACQuery(this, uri, callback, mainThreadResponse); 1.345 + 1.346 + if (IsPACURI(uri)) { 1.347 + // deal with this directly instead of queueing it 1.348 + query->Complete(NS_OK, EmptyCString()); 1.349 + return NS_OK; 1.350 + } 1.351 + 1.352 + return mPACThread->Dispatch(query, nsIEventTarget::DISPATCH_NORMAL); 1.353 +} 1.354 + 1.355 +nsresult 1.356 +nsPACMan::PostQuery(PendingPACQuery *query) 1.357 +{ 1.358 + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); 1.359 + 1.360 + if (mShutdown) { 1.361 + query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString()); 1.362 + return NS_OK; 1.363 + } 1.364 + 1.365 + // add a reference to the query while it is in the pending list 1.366 + nsRefPtr<PendingPACQuery> addref(query); 1.367 + mPendingQ.insertBack(addref.forget().take()); 1.368 + ProcessPendingQ(); 1.369 + return NS_OK; 1.370 +} 1.371 + 1.372 +nsresult 1.373 +nsPACMan::LoadPACFromURI(const nsCString &spec) 1.374 +{ 1.375 + NS_ENSURE_STATE(!mShutdown); 1.376 + NS_ENSURE_ARG(!spec.IsEmpty() || !mPACURISpec.IsEmpty()); 1.377 + 1.378 + nsCOMPtr<nsIStreamLoader> loader = 1.379 + do_CreateInstance(NS_STREAMLOADER_CONTRACTID); 1.380 + NS_ENSURE_STATE(loader); 1.381 + 1.382 + // Since we might get called from nsProtocolProxyService::Init, we need to 1.383 + // post an event back to the main thread before we try to use the IO service. 1.384 + // 1.385 + // But, we need to flag ourselves as loading, so that we queue up any PAC 1.386 + // queries the enter between now and when we actually load the PAC file. 1.387 + 1.388 + if (!mLoadPending) { 1.389 + nsCOMPtr<nsIRunnable> event = 1.390 + NS_NewRunnableMethod(this, &nsPACMan::StartLoading); 1.391 + nsresult rv; 1.392 + if (NS_FAILED(rv = NS_DispatchToCurrentThread(event))) 1.393 + return rv; 1.394 + mLoadPending = true; 1.395 + } 1.396 + 1.397 + CancelExistingLoad(); 1.398 + 1.399 + mLoader = loader; 1.400 + if (!spec.IsEmpty()) { 1.401 + mPACURISpec = spec; 1.402 + mPACURIRedirectSpec.Truncate(); 1.403 + mNormalPACURISpec.Truncate(); // set at load time 1.404 + mLoadFailureCount = 0; // reset 1.405 + } 1.406 + 1.407 + // reset to Null 1.408 + mScheduledReload = TimeStamp(); 1.409 + return NS_OK; 1.410 +} 1.411 + 1.412 +void 1.413 +nsPACMan::StartLoading() 1.414 +{ 1.415 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.416 + mLoadPending = false; 1.417 + 1.418 + // CancelExistingLoad was called... 1.419 + if (!mLoader) { 1.420 + PostCancelPendingQ(NS_ERROR_ABORT); 1.421 + return; 1.422 + } 1.423 + 1.424 + if (NS_SUCCEEDED(mLoader->Init(this))) { 1.425 + // Always hit the origin server when loading PAC. 1.426 + nsCOMPtr<nsIIOService> ios = do_GetIOService(); 1.427 + if (ios) { 1.428 + nsCOMPtr<nsIChannel> channel; 1.429 + nsCOMPtr<nsIURI> pacURI; 1.430 + NS_NewURI(getter_AddRefs(pacURI), mPACURISpec); 1.431 + 1.432 + // NOTE: This results in GetProxyForURI being called 1.433 + if (pacURI) { 1.434 + pacURI->GetSpec(mNormalPACURISpec); 1.435 + ios->NewChannelFromURI(pacURI, getter_AddRefs(channel)); 1.436 + } 1.437 + else { 1.438 + LOG(("nsPACMan::StartLoading Failed pacspec uri conversion %s\n", 1.439 + mPACURISpec.get())); 1.440 + } 1.441 + 1.442 + if (channel) { 1.443 + channel->SetLoadFlags(nsIRequest::LOAD_BYPASS_CACHE); 1.444 + channel->SetNotificationCallbacks(this); 1.445 + if (NS_SUCCEEDED(channel->AsyncOpen(mLoader, nullptr))) 1.446 + return; 1.447 + } 1.448 + } 1.449 + } 1.450 + 1.451 + CancelExistingLoad(); 1.452 + PostCancelPendingQ(NS_ERROR_UNEXPECTED); 1.453 +} 1.454 + 1.455 + 1.456 +void 1.457 +nsPACMan::OnLoadFailure() 1.458 +{ 1.459 + int32_t minInterval = 5; // 5 seconds 1.460 + int32_t maxInterval = 300; // 5 minutes 1.461 + 1.462 + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.463 + if (prefs) { 1.464 + prefs->GetIntPref("network.proxy.autoconfig_retry_interval_min", 1.465 + &minInterval); 1.466 + prefs->GetIntPref("network.proxy.autoconfig_retry_interval_max", 1.467 + &maxInterval); 1.468 + } 1.469 + 1.470 + int32_t interval = minInterval << mLoadFailureCount++; // seconds 1.471 + if (!interval || interval > maxInterval) 1.472 + interval = maxInterval; 1.473 + 1.474 + mScheduledReload = TimeStamp::Now() + TimeDuration::FromSeconds(interval); 1.475 + 1.476 + // while we wait for the retry queued members should try direct 1.477 + // even if that means fast failure. 1.478 + PostCancelPendingQ(NS_ERROR_NOT_AVAILABLE); 1.479 +} 1.480 + 1.481 +void 1.482 +nsPACMan::CancelExistingLoad() 1.483 +{ 1.484 + if (mLoader) { 1.485 + nsCOMPtr<nsIRequest> request; 1.486 + mLoader->GetRequest(getter_AddRefs(request)); 1.487 + if (request) 1.488 + request->Cancel(NS_ERROR_ABORT); 1.489 + mLoader = nullptr; 1.490 + } 1.491 +} 1.492 + 1.493 +void 1.494 +nsPACMan::PostProcessPendingQ() 1.495 +{ 1.496 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.497 + nsRefPtr<ExecutePACThreadAction> pending = 1.498 + new ExecutePACThreadAction(this); 1.499 + if (mPACThread) 1.500 + mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); 1.501 +} 1.502 + 1.503 +void 1.504 +nsPACMan::PostCancelPendingQ(nsresult status) 1.505 +{ 1.506 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.507 + nsRefPtr<ExecutePACThreadAction> pending = 1.508 + new ExecutePACThreadAction(this); 1.509 + pending->CancelQueue(status); 1.510 + if (mPACThread) 1.511 + mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); 1.512 +} 1.513 + 1.514 +void 1.515 +nsPACMan::CancelPendingQ(nsresult status) 1.516 +{ 1.517 + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); 1.518 + nsRefPtr<PendingPACQuery> query; 1.519 + 1.520 + while (!mPendingQ.isEmpty()) { 1.521 + query = dont_AddRef(mPendingQ.popLast()); 1.522 + query->Complete(status, EmptyCString()); 1.523 + } 1.524 + 1.525 + if (mShutdown) 1.526 + mPAC.Shutdown(); 1.527 +} 1.528 + 1.529 +void 1.530 +nsPACMan::ProcessPendingQ() 1.531 +{ 1.532 + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); 1.533 + while (ProcessPending()); 1.534 + 1.535 + if (mShutdown) { 1.536 + mPAC.Shutdown(); 1.537 + } else { 1.538 + // do GC while the thread has nothing pending 1.539 + mPAC.GC(); 1.540 + } 1.541 +} 1.542 + 1.543 +// returns true if progress was made by shortening the queue 1.544 +bool 1.545 +nsPACMan::ProcessPending() 1.546 +{ 1.547 + if (mPendingQ.isEmpty()) 1.548 + return false; 1.549 + 1.550 + // queue during normal load, but if we are retrying a failed load then 1.551 + // fast fail the queries 1.552 + if (mInProgress || (IsLoading() && !mLoadFailureCount)) 1.553 + return false; 1.554 + 1.555 + nsRefPtr<PendingPACQuery> query(dont_AddRef(mPendingQ.popFirst())); 1.556 + 1.557 + if (mShutdown || IsLoading()) { 1.558 + query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString()); 1.559 + return true; 1.560 + } 1.561 + 1.562 + nsAutoCString pacString; 1.563 + bool completed = false; 1.564 + mInProgress = true; 1.565 + nsAutoCString PACURI; 1.566 + 1.567 + // first we need to consider the system proxy changing the pac url 1.568 + if (mSystemProxySettings && 1.569 + NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) && 1.570 + !PACURI.IsEmpty() && 1.571 + !PACURI.Equals(mPACURISpec)) { 1.572 + query->UseAlternatePACFile(PACURI); 1.573 + completed = true; 1.574 + } 1.575 + 1.576 + // now try the system proxy settings for this particular url if 1.577 + // PAC was not specified 1.578 + if (!completed && mSystemProxySettings && PACURI.IsEmpty() && 1.579 + NS_SUCCEEDED(mSystemProxySettings-> 1.580 + GetProxyForURI(query->mSpec, query->mScheme, 1.581 + query->mHost, query->mPort, 1.582 + pacString))) { 1.583 + query->Complete(NS_OK, pacString); 1.584 + completed = true; 1.585 + } 1.586 + 1.587 + // the systemproxysettings didn't complete the resolution. try via PAC 1.588 + if (!completed) { 1.589 + nsresult status = mPAC.GetProxyForURI(query->mSpec, query->mHost, pacString); 1.590 + query->Complete(status, pacString); 1.591 + } 1.592 + 1.593 + mInProgress = false; 1.594 + return true; 1.595 +} 1.596 + 1.597 +NS_IMPL_ISUPPORTS(nsPACMan, nsIStreamLoaderObserver, 1.598 + nsIInterfaceRequestor, nsIChannelEventSink) 1.599 + 1.600 +NS_IMETHODIMP 1.601 +nsPACMan::OnStreamComplete(nsIStreamLoader *loader, 1.602 + nsISupports *context, 1.603 + nsresult status, 1.604 + uint32_t dataLen, 1.605 + const uint8_t *data) 1.606 +{ 1.607 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.608 + if (mLoader != loader) { 1.609 + // If this happens, then it means that LoadPACFromURI was called more 1.610 + // than once before the initial call completed. In this case, status 1.611 + // should be NS_ERROR_ABORT, and if so, then we know that we can and 1.612 + // should delay any processing. 1.613 + if (status == NS_ERROR_ABORT) 1.614 + return NS_OK; 1.615 + } 1.616 + 1.617 + if (NS_SUCCEEDED(status) && HttpRequestSucceeded(loader)) { 1.618 + // Get the URI spec used to load this PAC script. 1.619 + nsAutoCString pacURI; 1.620 + { 1.621 + nsCOMPtr<nsIRequest> request; 1.622 + loader->GetRequest(getter_AddRefs(request)); 1.623 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.624 + if (channel) { 1.625 + nsCOMPtr<nsIURI> uri; 1.626 + channel->GetURI(getter_AddRefs(uri)); 1.627 + if (uri) 1.628 + uri->GetAsciiSpec(pacURI); 1.629 + } 1.630 + } 1.631 + 1.632 + // We assume that the PAC text is ASCII (or ISO-Latin-1). We've had this 1.633 + // assumption forever, and some real-world PAC scripts actually have some 1.634 + // non-ASCII text in comment blocks (see bug 296163). 1.635 + const char *text = (const char *) data; 1.636 + 1.637 + // we have succeeded in loading the pac file using a bunch of interfaces that 1.638 + // are main thread only, unfortunately we have to initialize the instance of 1.639 + // the PAC evaluator (NS_PROXYAUTOCONFIG_CONTRACTID) on the pac thread, because 1.640 + // that is where it will be used. 1.641 + 1.642 + nsRefPtr<ExecutePACThreadAction> pending = 1.643 + new ExecutePACThreadAction(this); 1.644 + pending->SetupPAC(text, dataLen, pacURI); 1.645 + if (mPACThread) 1.646 + mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); 1.647 + 1.648 + // Even if the PAC file could not be parsed, we did succeed in loading the 1.649 + // data for it. 1.650 + mLoadFailureCount = 0; 1.651 + } else { 1.652 + // We were unable to load the PAC file (presumably because of a network 1.653 + // failure). Try again a little later. 1.654 + OnLoadFailure(); 1.655 + } 1.656 + 1.657 + if (NS_SUCCEEDED(status)) 1.658 + PostProcessPendingQ(); 1.659 + else 1.660 + PostCancelPendingQ(status); 1.661 + 1.662 + return NS_OK; 1.663 +} 1.664 + 1.665 +NS_IMETHODIMP 1.666 +nsPACMan::GetInterface(const nsIID &iid, void **result) 1.667 +{ 1.668 + // In case loading the PAC file requires authentication. 1.669 + if (iid.Equals(NS_GET_IID(nsIAuthPrompt))) { 1.670 + nsCOMPtr<nsIPromptFactory> promptFac = do_GetService("@mozilla.org/prompter;1"); 1.671 + NS_ENSURE_TRUE(promptFac, NS_ERROR_FAILURE); 1.672 + return promptFac->GetPrompt(nullptr, iid, reinterpret_cast<void**>(result)); 1.673 + } 1.674 + 1.675 + // In case loading the PAC file results in a redirect. 1.676 + if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) { 1.677 + NS_ADDREF_THIS(); 1.678 + *result = static_cast<nsIChannelEventSink *>(this); 1.679 + return NS_OK; 1.680 + } 1.681 + 1.682 + return NS_ERROR_NO_INTERFACE; 1.683 +} 1.684 + 1.685 +NS_IMETHODIMP 1.686 +nsPACMan::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, 1.687 + uint32_t flags, 1.688 + nsIAsyncVerifyRedirectCallback *callback) 1.689 +{ 1.690 + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); 1.691 + 1.692 + nsresult rv = NS_OK; 1.693 + nsCOMPtr<nsIURI> pacURI; 1.694 + if (NS_FAILED((rv = newChannel->GetURI(getter_AddRefs(pacURI))))) 1.695 + return rv; 1.696 + 1.697 + rv = pacURI->GetSpec(mPACURIRedirectSpec); 1.698 + if (NS_FAILED(rv)) 1.699 + return rv; 1.700 + 1.701 + LOG(("nsPACMan redirect from original %s to redirected %s\n", 1.702 + mPACURISpec.get(), mPACURIRedirectSpec.get())); 1.703 + 1.704 + // do not update mPACURISpec - that needs to stay as the 1.705 + // configured URI so that we can determine when the config changes. 1.706 + // However do track the most recent URI in the redirect change 1.707 + // as mPACURIRedirectSpec so that URI can be allowed to bypass 1.708 + // the proxy and actually fetch the pac file. 1.709 + 1.710 + callback->OnRedirectVerifyCallback(NS_OK); 1.711 + return NS_OK; 1.712 +} 1.713 + 1.714 +void 1.715 +nsPACMan::NamePACThread() 1.716 +{ 1.717 + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); 1.718 + PR_SetCurrentThreadName("Proxy Resolution"); 1.719 +#ifdef MOZ_NUWA_PROCESS 1.720 + if (IsNuwaProcess()) { 1.721 + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, 1.722 + "NuwaMarkCurrentThread is undefined!"); 1.723 + NuwaMarkCurrentThread(nullptr, nullptr); 1.724 + } 1.725 +#endif 1.726 +} 1.727 + 1.728 +nsresult 1.729 +nsPACMan::Init(nsISystemProxySettings *systemProxySettings) 1.730 +{ 1.731 + mSystemProxySettings = systemProxySettings; 1.732 + 1.733 + nsresult rv = NS_NewThread(getter_AddRefs(mPACThread), nullptr); 1.734 + if (NS_FAILED(rv)) 1.735 + return rv; 1.736 + 1.737 + nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &nsPACMan::NamePACThread); 1.738 + // don't check return value as it is not a big deal for this to fail. 1.739 + mPACThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); 1.740 + 1.741 + return NS_OK; 1.742 +} 1.743 + 1.744 +namespace mozilla { 1.745 +namespace net { 1.746 + 1.747 +PRLogModuleInfo* 1.748 +GetProxyLog() 1.749 +{ 1.750 + static PRLogModuleInfo *sLog; 1.751 + if (!sLog) 1.752 + sLog = PR_NewLogModule("proxy"); 1.753 + return sLog; 1.754 +} 1.755 + 1.756 +} 1.757 +}