netwerk/base/src/nsPACMan.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: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsPACMan.h"
michael@0 8 #include "nsThreadUtils.h"
michael@0 9 #include "nsIAuthPrompt.h"
michael@0 10 #include "nsIPromptFactory.h"
michael@0 11 #include "nsIHttpChannel.h"
michael@0 12 #include "nsIPrefService.h"
michael@0 13 #include "nsIPrefBranch.h"
michael@0 14 #include "nsNetUtil.h"
michael@0 15 #include "nsIAsyncVerifyRedirectCallback.h"
michael@0 16 #include "nsISystemProxySettings.h"
michael@0 17 #ifdef MOZ_NUWA_PROCESS
michael@0 18 #include "ipc/Nuwa.h"
michael@0 19 #endif
michael@0 20
michael@0 21 //-----------------------------------------------------------------------------
michael@0 22 using namespace mozilla;
michael@0 23 using namespace mozilla::net;
michael@0 24
michael@0 25 #if defined(PR_LOGGING)
michael@0 26 #endif
michael@0 27 #undef LOG
michael@0 28 #define LOG(args) PR_LOG(GetProxyLog(), PR_LOG_DEBUG, args)
michael@0 29
michael@0 30 // The PAC thread does evaluations of both PAC files and
michael@0 31 // nsISystemProxySettings because they can both block the calling thread and we
michael@0 32 // don't want that on the main thread
michael@0 33
michael@0 34 // Check to see if the underlying request was not an error page in the case of
michael@0 35 // a HTTP request. For other types of channels, just return true.
michael@0 36 static bool
michael@0 37 HttpRequestSucceeded(nsIStreamLoader *loader)
michael@0 38 {
michael@0 39 nsCOMPtr<nsIRequest> request;
michael@0 40 loader->GetRequest(getter_AddRefs(request));
michael@0 41
michael@0 42 bool result = true; // default to assuming success
michael@0 43
michael@0 44 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
michael@0 45 if (httpChannel)
michael@0 46 httpChannel->GetRequestSucceeded(&result);
michael@0 47
michael@0 48 return result;
michael@0 49 }
michael@0 50
michael@0 51 //-----------------------------------------------------------------------------
michael@0 52
michael@0 53 // The ExecuteCallback runnable is triggered by
michael@0 54 // nsPACManCallback::OnQueryComplete on the Main thread when its completion is
michael@0 55 // discovered on the pac thread
michael@0 56
michael@0 57 class ExecuteCallback MOZ_FINAL : public nsRunnable
michael@0 58 {
michael@0 59 public:
michael@0 60 ExecuteCallback(nsPACManCallback *aCallback,
michael@0 61 nsresult status)
michael@0 62 : mCallback(aCallback)
michael@0 63 , mStatus(status)
michael@0 64 {
michael@0 65 }
michael@0 66
michael@0 67 void SetPACString(const nsCString &pacString)
michael@0 68 {
michael@0 69 mPACString = pacString;
michael@0 70 }
michael@0 71
michael@0 72 void SetPACURL(const nsCString &pacURL)
michael@0 73 {
michael@0 74 mPACURL = pacURL;
michael@0 75 }
michael@0 76
michael@0 77 NS_IMETHODIMP Run()
michael@0 78 {
michael@0 79 mCallback->OnQueryComplete(mStatus, mPACString, mPACURL);
michael@0 80 mCallback = nullptr;
michael@0 81 return NS_OK;
michael@0 82 }
michael@0 83
michael@0 84 private:
michael@0 85 nsRefPtr<nsPACManCallback> mCallback;
michael@0 86 nsresult mStatus;
michael@0 87 nsCString mPACString;
michael@0 88 nsCString mPACURL;
michael@0 89 };
michael@0 90
michael@0 91 //-----------------------------------------------------------------------------
michael@0 92
michael@0 93 // The PAC thread must be deleted from the main thread, this class
michael@0 94 // acts as a proxy to do that, as the PACMan is reference counted
michael@0 95 // and might be destroyed on either thread
michael@0 96
michael@0 97 class ShutdownThread MOZ_FINAL : public nsRunnable
michael@0 98 {
michael@0 99 public:
michael@0 100 ShutdownThread(nsIThread *thread)
michael@0 101 : mThread(thread)
michael@0 102 {
michael@0 103 }
michael@0 104
michael@0 105 NS_IMETHODIMP Run()
michael@0 106 {
michael@0 107 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 108 mThread->Shutdown();
michael@0 109 return NS_OK;
michael@0 110 }
michael@0 111
michael@0 112 private:
michael@0 113 nsCOMPtr<nsIThread> mThread;
michael@0 114 };
michael@0 115
michael@0 116 // Dispatch this to wait until the PAC thread shuts down.
michael@0 117
michael@0 118 class WaitForThreadShutdown MOZ_FINAL : public nsRunnable
michael@0 119 {
michael@0 120 public:
michael@0 121 explicit WaitForThreadShutdown(nsPACMan *aPACMan)
michael@0 122 : mPACMan(aPACMan)
michael@0 123 {
michael@0 124 }
michael@0 125
michael@0 126 NS_IMETHODIMP Run()
michael@0 127 {
michael@0 128 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 129 if (mPACMan->mPACThread) {
michael@0 130 mPACMan->mPACThread->Shutdown();
michael@0 131 mPACMan->mPACThread = nullptr;
michael@0 132 }
michael@0 133 return NS_OK;
michael@0 134 }
michael@0 135
michael@0 136 private:
michael@0 137 nsRefPtr<nsPACMan> mPACMan;
michael@0 138 };
michael@0 139
michael@0 140 //-----------------------------------------------------------------------------
michael@0 141
michael@0 142 // PACLoadComplete allows the PAC thread to tell the main thread that
michael@0 143 // the javascript PAC file has been installed (perhaps unsuccessfully)
michael@0 144 // and that there is no reason to queue executions anymore
michael@0 145
michael@0 146 class PACLoadComplete MOZ_FINAL : public nsRunnable
michael@0 147 {
michael@0 148 public:
michael@0 149 PACLoadComplete(nsPACMan *aPACMan)
michael@0 150 : mPACMan(aPACMan)
michael@0 151 {
michael@0 152 }
michael@0 153
michael@0 154 NS_IMETHODIMP Run()
michael@0 155 {
michael@0 156 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 157 mPACMan->mLoader = nullptr;
michael@0 158 mPACMan->PostProcessPendingQ();
michael@0 159 return NS_OK;
michael@0 160 }
michael@0 161
michael@0 162 private:
michael@0 163 nsRefPtr<nsPACMan> mPACMan;
michael@0 164 };
michael@0 165
michael@0 166 //-----------------------------------------------------------------------------
michael@0 167
michael@0 168 // ExecutePACThreadAction is used to proxy actions from the main
michael@0 169 // thread onto the PAC thread. There are 3 options: process the queue,
michael@0 170 // cancel the queue, and setup the javascript context with a new PAC file
michael@0 171
michael@0 172 class ExecutePACThreadAction MOZ_FINAL : public nsRunnable
michael@0 173 {
michael@0 174 public:
michael@0 175 // by default we just process the queue
michael@0 176 ExecutePACThreadAction(nsPACMan *aPACMan)
michael@0 177 : mPACMan(aPACMan)
michael@0 178 , mCancel(false)
michael@0 179 , mSetupPAC(false)
michael@0 180 { }
michael@0 181
michael@0 182 void CancelQueue (nsresult status)
michael@0 183 {
michael@0 184 mCancel = true;
michael@0 185 mCancelStatus = status;
michael@0 186 }
michael@0 187
michael@0 188 void SetupPAC (const char *text, uint32_t datalen, nsCString &pacURI)
michael@0 189 {
michael@0 190 mSetupPAC = true;
michael@0 191 mSetupPACData.Assign(text, datalen);
michael@0 192 mSetupPACURI = pacURI;
michael@0 193 }
michael@0 194
michael@0 195 NS_IMETHODIMP Run()
michael@0 196 {
michael@0 197 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread");
michael@0 198 if (mCancel) {
michael@0 199 mPACMan->CancelPendingQ(mCancelStatus);
michael@0 200 mCancel = false;
michael@0 201 return NS_OK;
michael@0 202 }
michael@0 203
michael@0 204 if (mSetupPAC) {
michael@0 205 mSetupPAC = false;
michael@0 206
michael@0 207 mPACMan->mPAC.Init(mSetupPACURI,
michael@0 208 mSetupPACData);
michael@0 209
michael@0 210 nsRefPtr<PACLoadComplete> runnable = new PACLoadComplete(mPACMan);
michael@0 211 NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
michael@0 212 return NS_OK;
michael@0 213 }
michael@0 214
michael@0 215 mPACMan->ProcessPendingQ();
michael@0 216 return NS_OK;
michael@0 217 }
michael@0 218
michael@0 219 private:
michael@0 220 nsRefPtr<nsPACMan> mPACMan;
michael@0 221
michael@0 222 bool mCancel;
michael@0 223 nsresult mCancelStatus;
michael@0 224
michael@0 225 bool mSetupPAC;
michael@0 226 nsCString mSetupPACData;
michael@0 227 nsCString mSetupPACURI;
michael@0 228 };
michael@0 229
michael@0 230 //-----------------------------------------------------------------------------
michael@0 231
michael@0 232 PendingPACQuery::PendingPACQuery(nsPACMan *pacMan, nsIURI *uri,
michael@0 233 nsPACManCallback *callback,
michael@0 234 bool mainThreadResponse)
michael@0 235 : mPACMan(pacMan)
michael@0 236 , mCallback(callback)
michael@0 237 , mOnMainThreadOnly(mainThreadResponse)
michael@0 238 {
michael@0 239 uri->GetAsciiSpec(mSpec);
michael@0 240 uri->GetAsciiHost(mHost);
michael@0 241 uri->GetScheme(mScheme);
michael@0 242 uri->GetPort(&mPort);
michael@0 243 }
michael@0 244
michael@0 245 void
michael@0 246 PendingPACQuery::Complete(nsresult status, const nsCString &pacString)
michael@0 247 {
michael@0 248 if (!mCallback)
michael@0 249 return;
michael@0 250 nsRefPtr<ExecuteCallback> runnable = new ExecuteCallback(mCallback, status);
michael@0 251 runnable->SetPACString(pacString);
michael@0 252 if (mOnMainThreadOnly)
michael@0 253 NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
michael@0 254 else
michael@0 255 runnable->Run();
michael@0 256 }
michael@0 257
michael@0 258 void
michael@0 259 PendingPACQuery::UseAlternatePACFile(const nsCString &pacURL)
michael@0 260 {
michael@0 261 if (!mCallback)
michael@0 262 return;
michael@0 263
michael@0 264 nsRefPtr<ExecuteCallback> runnable = new ExecuteCallback(mCallback, NS_OK);
michael@0 265 runnable->SetPACURL(pacURL);
michael@0 266 if (mOnMainThreadOnly)
michael@0 267 NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
michael@0 268 else
michael@0 269 runnable->Run();
michael@0 270 }
michael@0 271
michael@0 272 NS_IMETHODIMP
michael@0 273 PendingPACQuery::Run()
michael@0 274 {
michael@0 275 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread");
michael@0 276 mPACMan->PostQuery(this);
michael@0 277 return NS_OK;
michael@0 278 }
michael@0 279
michael@0 280 //-----------------------------------------------------------------------------
michael@0 281
michael@0 282 nsPACMan::nsPACMan()
michael@0 283 : mLoadPending(false)
michael@0 284 , mShutdown(false)
michael@0 285 , mLoadFailureCount(0)
michael@0 286 , mInProgress(false)
michael@0 287 {
michael@0 288 NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be created on main thread");
michael@0 289 }
michael@0 290
michael@0 291 nsPACMan::~nsPACMan()
michael@0 292 {
michael@0 293 if (mPACThread) {
michael@0 294 if (NS_IsMainThread()) {
michael@0 295 mPACThread->Shutdown();
michael@0 296 }
michael@0 297 else {
michael@0 298 nsRefPtr<ShutdownThread> runnable = new ShutdownThread(mPACThread);
michael@0 299 NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
michael@0 300 }
michael@0 301 }
michael@0 302
michael@0 303 NS_ASSERTION(mLoader == nullptr, "pac man not shutdown properly");
michael@0 304 NS_ASSERTION(mPendingQ.isEmpty(), "pac man not shutdown properly");
michael@0 305 }
michael@0 306
michael@0 307 void
michael@0 308 nsPACMan::Shutdown()
michael@0 309 {
michael@0 310 NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be shutdown on main thread");
michael@0 311 if (mShutdown) {
michael@0 312 return;
michael@0 313 }
michael@0 314 mShutdown = true;
michael@0 315 CancelExistingLoad();
michael@0 316 PostCancelPendingQ(NS_ERROR_ABORT);
michael@0 317
michael@0 318 nsRefPtr<WaitForThreadShutdown> runnable = new WaitForThreadShutdown(this);
michael@0 319 NS_DispatchToMainThread(runnable);
michael@0 320 }
michael@0 321
michael@0 322 nsresult
michael@0 323 nsPACMan::AsyncGetProxyForChannel(nsIChannel *channel, nsPACManCallback *callback,
michael@0 324 bool mainThreadResponse)
michael@0 325 {
michael@0 326 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 327 if (mShutdown)
michael@0 328 return NS_ERROR_NOT_AVAILABLE;
michael@0 329
michael@0 330 // Maybe Reload PAC
michael@0 331 if (!mPACURISpec.IsEmpty() && !mScheduledReload.IsNull() &&
michael@0 332 TimeStamp::Now() > mScheduledReload)
michael@0 333 LoadPACFromURI(EmptyCString());
michael@0 334
michael@0 335 nsCOMPtr<nsIURI> uri;
michael@0 336 nsresult rv = channel->GetURI(getter_AddRefs(uri));
michael@0 337 if (NS_FAILED(rv))
michael@0 338 return rv;
michael@0 339
michael@0 340 nsRefPtr<PendingPACQuery> query =
michael@0 341 new PendingPACQuery(this, uri, callback, mainThreadResponse);
michael@0 342
michael@0 343 if (IsPACURI(uri)) {
michael@0 344 // deal with this directly instead of queueing it
michael@0 345 query->Complete(NS_OK, EmptyCString());
michael@0 346 return NS_OK;
michael@0 347 }
michael@0 348
michael@0 349 return mPACThread->Dispatch(query, nsIEventTarget::DISPATCH_NORMAL);
michael@0 350 }
michael@0 351
michael@0 352 nsresult
michael@0 353 nsPACMan::PostQuery(PendingPACQuery *query)
michael@0 354 {
michael@0 355 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread");
michael@0 356
michael@0 357 if (mShutdown) {
michael@0 358 query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString());
michael@0 359 return NS_OK;
michael@0 360 }
michael@0 361
michael@0 362 // add a reference to the query while it is in the pending list
michael@0 363 nsRefPtr<PendingPACQuery> addref(query);
michael@0 364 mPendingQ.insertBack(addref.forget().take());
michael@0 365 ProcessPendingQ();
michael@0 366 return NS_OK;
michael@0 367 }
michael@0 368
michael@0 369 nsresult
michael@0 370 nsPACMan::LoadPACFromURI(const nsCString &spec)
michael@0 371 {
michael@0 372 NS_ENSURE_STATE(!mShutdown);
michael@0 373 NS_ENSURE_ARG(!spec.IsEmpty() || !mPACURISpec.IsEmpty());
michael@0 374
michael@0 375 nsCOMPtr<nsIStreamLoader> loader =
michael@0 376 do_CreateInstance(NS_STREAMLOADER_CONTRACTID);
michael@0 377 NS_ENSURE_STATE(loader);
michael@0 378
michael@0 379 // Since we might get called from nsProtocolProxyService::Init, we need to
michael@0 380 // post an event back to the main thread before we try to use the IO service.
michael@0 381 //
michael@0 382 // But, we need to flag ourselves as loading, so that we queue up any PAC
michael@0 383 // queries the enter between now and when we actually load the PAC file.
michael@0 384
michael@0 385 if (!mLoadPending) {
michael@0 386 nsCOMPtr<nsIRunnable> event =
michael@0 387 NS_NewRunnableMethod(this, &nsPACMan::StartLoading);
michael@0 388 nsresult rv;
michael@0 389 if (NS_FAILED(rv = NS_DispatchToCurrentThread(event)))
michael@0 390 return rv;
michael@0 391 mLoadPending = true;
michael@0 392 }
michael@0 393
michael@0 394 CancelExistingLoad();
michael@0 395
michael@0 396 mLoader = loader;
michael@0 397 if (!spec.IsEmpty()) {
michael@0 398 mPACURISpec = spec;
michael@0 399 mPACURIRedirectSpec.Truncate();
michael@0 400 mNormalPACURISpec.Truncate(); // set at load time
michael@0 401 mLoadFailureCount = 0; // reset
michael@0 402 }
michael@0 403
michael@0 404 // reset to Null
michael@0 405 mScheduledReload = TimeStamp();
michael@0 406 return NS_OK;
michael@0 407 }
michael@0 408
michael@0 409 void
michael@0 410 nsPACMan::StartLoading()
michael@0 411 {
michael@0 412 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 413 mLoadPending = false;
michael@0 414
michael@0 415 // CancelExistingLoad was called...
michael@0 416 if (!mLoader) {
michael@0 417 PostCancelPendingQ(NS_ERROR_ABORT);
michael@0 418 return;
michael@0 419 }
michael@0 420
michael@0 421 if (NS_SUCCEEDED(mLoader->Init(this))) {
michael@0 422 // Always hit the origin server when loading PAC.
michael@0 423 nsCOMPtr<nsIIOService> ios = do_GetIOService();
michael@0 424 if (ios) {
michael@0 425 nsCOMPtr<nsIChannel> channel;
michael@0 426 nsCOMPtr<nsIURI> pacURI;
michael@0 427 NS_NewURI(getter_AddRefs(pacURI), mPACURISpec);
michael@0 428
michael@0 429 // NOTE: This results in GetProxyForURI being called
michael@0 430 if (pacURI) {
michael@0 431 pacURI->GetSpec(mNormalPACURISpec);
michael@0 432 ios->NewChannelFromURI(pacURI, getter_AddRefs(channel));
michael@0 433 }
michael@0 434 else {
michael@0 435 LOG(("nsPACMan::StartLoading Failed pacspec uri conversion %s\n",
michael@0 436 mPACURISpec.get()));
michael@0 437 }
michael@0 438
michael@0 439 if (channel) {
michael@0 440 channel->SetLoadFlags(nsIRequest::LOAD_BYPASS_CACHE);
michael@0 441 channel->SetNotificationCallbacks(this);
michael@0 442 if (NS_SUCCEEDED(channel->AsyncOpen(mLoader, nullptr)))
michael@0 443 return;
michael@0 444 }
michael@0 445 }
michael@0 446 }
michael@0 447
michael@0 448 CancelExistingLoad();
michael@0 449 PostCancelPendingQ(NS_ERROR_UNEXPECTED);
michael@0 450 }
michael@0 451
michael@0 452
michael@0 453 void
michael@0 454 nsPACMan::OnLoadFailure()
michael@0 455 {
michael@0 456 int32_t minInterval = 5; // 5 seconds
michael@0 457 int32_t maxInterval = 300; // 5 minutes
michael@0 458
michael@0 459 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 460 if (prefs) {
michael@0 461 prefs->GetIntPref("network.proxy.autoconfig_retry_interval_min",
michael@0 462 &minInterval);
michael@0 463 prefs->GetIntPref("network.proxy.autoconfig_retry_interval_max",
michael@0 464 &maxInterval);
michael@0 465 }
michael@0 466
michael@0 467 int32_t interval = minInterval << mLoadFailureCount++; // seconds
michael@0 468 if (!interval || interval > maxInterval)
michael@0 469 interval = maxInterval;
michael@0 470
michael@0 471 mScheduledReload = TimeStamp::Now() + TimeDuration::FromSeconds(interval);
michael@0 472
michael@0 473 // while we wait for the retry queued members should try direct
michael@0 474 // even if that means fast failure.
michael@0 475 PostCancelPendingQ(NS_ERROR_NOT_AVAILABLE);
michael@0 476 }
michael@0 477
michael@0 478 void
michael@0 479 nsPACMan::CancelExistingLoad()
michael@0 480 {
michael@0 481 if (mLoader) {
michael@0 482 nsCOMPtr<nsIRequest> request;
michael@0 483 mLoader->GetRequest(getter_AddRefs(request));
michael@0 484 if (request)
michael@0 485 request->Cancel(NS_ERROR_ABORT);
michael@0 486 mLoader = nullptr;
michael@0 487 }
michael@0 488 }
michael@0 489
michael@0 490 void
michael@0 491 nsPACMan::PostProcessPendingQ()
michael@0 492 {
michael@0 493 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 494 nsRefPtr<ExecutePACThreadAction> pending =
michael@0 495 new ExecutePACThreadAction(this);
michael@0 496 if (mPACThread)
michael@0 497 mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL);
michael@0 498 }
michael@0 499
michael@0 500 void
michael@0 501 nsPACMan::PostCancelPendingQ(nsresult status)
michael@0 502 {
michael@0 503 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 504 nsRefPtr<ExecutePACThreadAction> pending =
michael@0 505 new ExecutePACThreadAction(this);
michael@0 506 pending->CancelQueue(status);
michael@0 507 if (mPACThread)
michael@0 508 mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL);
michael@0 509 }
michael@0 510
michael@0 511 void
michael@0 512 nsPACMan::CancelPendingQ(nsresult status)
michael@0 513 {
michael@0 514 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread");
michael@0 515 nsRefPtr<PendingPACQuery> query;
michael@0 516
michael@0 517 while (!mPendingQ.isEmpty()) {
michael@0 518 query = dont_AddRef(mPendingQ.popLast());
michael@0 519 query->Complete(status, EmptyCString());
michael@0 520 }
michael@0 521
michael@0 522 if (mShutdown)
michael@0 523 mPAC.Shutdown();
michael@0 524 }
michael@0 525
michael@0 526 void
michael@0 527 nsPACMan::ProcessPendingQ()
michael@0 528 {
michael@0 529 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread");
michael@0 530 while (ProcessPending());
michael@0 531
michael@0 532 if (mShutdown) {
michael@0 533 mPAC.Shutdown();
michael@0 534 } else {
michael@0 535 // do GC while the thread has nothing pending
michael@0 536 mPAC.GC();
michael@0 537 }
michael@0 538 }
michael@0 539
michael@0 540 // returns true if progress was made by shortening the queue
michael@0 541 bool
michael@0 542 nsPACMan::ProcessPending()
michael@0 543 {
michael@0 544 if (mPendingQ.isEmpty())
michael@0 545 return false;
michael@0 546
michael@0 547 // queue during normal load, but if we are retrying a failed load then
michael@0 548 // fast fail the queries
michael@0 549 if (mInProgress || (IsLoading() && !mLoadFailureCount))
michael@0 550 return false;
michael@0 551
michael@0 552 nsRefPtr<PendingPACQuery> query(dont_AddRef(mPendingQ.popFirst()));
michael@0 553
michael@0 554 if (mShutdown || IsLoading()) {
michael@0 555 query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString());
michael@0 556 return true;
michael@0 557 }
michael@0 558
michael@0 559 nsAutoCString pacString;
michael@0 560 bool completed = false;
michael@0 561 mInProgress = true;
michael@0 562 nsAutoCString PACURI;
michael@0 563
michael@0 564 // first we need to consider the system proxy changing the pac url
michael@0 565 if (mSystemProxySettings &&
michael@0 566 NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
michael@0 567 !PACURI.IsEmpty() &&
michael@0 568 !PACURI.Equals(mPACURISpec)) {
michael@0 569 query->UseAlternatePACFile(PACURI);
michael@0 570 completed = true;
michael@0 571 }
michael@0 572
michael@0 573 // now try the system proxy settings for this particular url if
michael@0 574 // PAC was not specified
michael@0 575 if (!completed && mSystemProxySettings && PACURI.IsEmpty() &&
michael@0 576 NS_SUCCEEDED(mSystemProxySettings->
michael@0 577 GetProxyForURI(query->mSpec, query->mScheme,
michael@0 578 query->mHost, query->mPort,
michael@0 579 pacString))) {
michael@0 580 query->Complete(NS_OK, pacString);
michael@0 581 completed = true;
michael@0 582 }
michael@0 583
michael@0 584 // the systemproxysettings didn't complete the resolution. try via PAC
michael@0 585 if (!completed) {
michael@0 586 nsresult status = mPAC.GetProxyForURI(query->mSpec, query->mHost, pacString);
michael@0 587 query->Complete(status, pacString);
michael@0 588 }
michael@0 589
michael@0 590 mInProgress = false;
michael@0 591 return true;
michael@0 592 }
michael@0 593
michael@0 594 NS_IMPL_ISUPPORTS(nsPACMan, nsIStreamLoaderObserver,
michael@0 595 nsIInterfaceRequestor, nsIChannelEventSink)
michael@0 596
michael@0 597 NS_IMETHODIMP
michael@0 598 nsPACMan::OnStreamComplete(nsIStreamLoader *loader,
michael@0 599 nsISupports *context,
michael@0 600 nsresult status,
michael@0 601 uint32_t dataLen,
michael@0 602 const uint8_t *data)
michael@0 603 {
michael@0 604 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 605 if (mLoader != loader) {
michael@0 606 // If this happens, then it means that LoadPACFromURI was called more
michael@0 607 // than once before the initial call completed. In this case, status
michael@0 608 // should be NS_ERROR_ABORT, and if so, then we know that we can and
michael@0 609 // should delay any processing.
michael@0 610 if (status == NS_ERROR_ABORT)
michael@0 611 return NS_OK;
michael@0 612 }
michael@0 613
michael@0 614 if (NS_SUCCEEDED(status) && HttpRequestSucceeded(loader)) {
michael@0 615 // Get the URI spec used to load this PAC script.
michael@0 616 nsAutoCString pacURI;
michael@0 617 {
michael@0 618 nsCOMPtr<nsIRequest> request;
michael@0 619 loader->GetRequest(getter_AddRefs(request));
michael@0 620 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
michael@0 621 if (channel) {
michael@0 622 nsCOMPtr<nsIURI> uri;
michael@0 623 channel->GetURI(getter_AddRefs(uri));
michael@0 624 if (uri)
michael@0 625 uri->GetAsciiSpec(pacURI);
michael@0 626 }
michael@0 627 }
michael@0 628
michael@0 629 // We assume that the PAC text is ASCII (or ISO-Latin-1). We've had this
michael@0 630 // assumption forever, and some real-world PAC scripts actually have some
michael@0 631 // non-ASCII text in comment blocks (see bug 296163).
michael@0 632 const char *text = (const char *) data;
michael@0 633
michael@0 634 // we have succeeded in loading the pac file using a bunch of interfaces that
michael@0 635 // are main thread only, unfortunately we have to initialize the instance of
michael@0 636 // the PAC evaluator (NS_PROXYAUTOCONFIG_CONTRACTID) on the pac thread, because
michael@0 637 // that is where it will be used.
michael@0 638
michael@0 639 nsRefPtr<ExecutePACThreadAction> pending =
michael@0 640 new ExecutePACThreadAction(this);
michael@0 641 pending->SetupPAC(text, dataLen, pacURI);
michael@0 642 if (mPACThread)
michael@0 643 mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL);
michael@0 644
michael@0 645 // Even if the PAC file could not be parsed, we did succeed in loading the
michael@0 646 // data for it.
michael@0 647 mLoadFailureCount = 0;
michael@0 648 } else {
michael@0 649 // We were unable to load the PAC file (presumably because of a network
michael@0 650 // failure). Try again a little later.
michael@0 651 OnLoadFailure();
michael@0 652 }
michael@0 653
michael@0 654 if (NS_SUCCEEDED(status))
michael@0 655 PostProcessPendingQ();
michael@0 656 else
michael@0 657 PostCancelPendingQ(status);
michael@0 658
michael@0 659 return NS_OK;
michael@0 660 }
michael@0 661
michael@0 662 NS_IMETHODIMP
michael@0 663 nsPACMan::GetInterface(const nsIID &iid, void **result)
michael@0 664 {
michael@0 665 // In case loading the PAC file requires authentication.
michael@0 666 if (iid.Equals(NS_GET_IID(nsIAuthPrompt))) {
michael@0 667 nsCOMPtr<nsIPromptFactory> promptFac = do_GetService("@mozilla.org/prompter;1");
michael@0 668 NS_ENSURE_TRUE(promptFac, NS_ERROR_FAILURE);
michael@0 669 return promptFac->GetPrompt(nullptr, iid, reinterpret_cast<void**>(result));
michael@0 670 }
michael@0 671
michael@0 672 // In case loading the PAC file results in a redirect.
michael@0 673 if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
michael@0 674 NS_ADDREF_THIS();
michael@0 675 *result = static_cast<nsIChannelEventSink *>(this);
michael@0 676 return NS_OK;
michael@0 677 }
michael@0 678
michael@0 679 return NS_ERROR_NO_INTERFACE;
michael@0 680 }
michael@0 681
michael@0 682 NS_IMETHODIMP
michael@0 683 nsPACMan::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
michael@0 684 uint32_t flags,
michael@0 685 nsIAsyncVerifyRedirectCallback *callback)
michael@0 686 {
michael@0 687 NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread");
michael@0 688
michael@0 689 nsresult rv = NS_OK;
michael@0 690 nsCOMPtr<nsIURI> pacURI;
michael@0 691 if (NS_FAILED((rv = newChannel->GetURI(getter_AddRefs(pacURI)))))
michael@0 692 return rv;
michael@0 693
michael@0 694 rv = pacURI->GetSpec(mPACURIRedirectSpec);
michael@0 695 if (NS_FAILED(rv))
michael@0 696 return rv;
michael@0 697
michael@0 698 LOG(("nsPACMan redirect from original %s to redirected %s\n",
michael@0 699 mPACURISpec.get(), mPACURIRedirectSpec.get()));
michael@0 700
michael@0 701 // do not update mPACURISpec - that needs to stay as the
michael@0 702 // configured URI so that we can determine when the config changes.
michael@0 703 // However do track the most recent URI in the redirect change
michael@0 704 // as mPACURIRedirectSpec so that URI can be allowed to bypass
michael@0 705 // the proxy and actually fetch the pac file.
michael@0 706
michael@0 707 callback->OnRedirectVerifyCallback(NS_OK);
michael@0 708 return NS_OK;
michael@0 709 }
michael@0 710
michael@0 711 void
michael@0 712 nsPACMan::NamePACThread()
michael@0 713 {
michael@0 714 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread");
michael@0 715 PR_SetCurrentThreadName("Proxy Resolution");
michael@0 716 #ifdef MOZ_NUWA_PROCESS
michael@0 717 if (IsNuwaProcess()) {
michael@0 718 NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
michael@0 719 "NuwaMarkCurrentThread is undefined!");
michael@0 720 NuwaMarkCurrentThread(nullptr, nullptr);
michael@0 721 }
michael@0 722 #endif
michael@0 723 }
michael@0 724
michael@0 725 nsresult
michael@0 726 nsPACMan::Init(nsISystemProxySettings *systemProxySettings)
michael@0 727 {
michael@0 728 mSystemProxySettings = systemProxySettings;
michael@0 729
michael@0 730 nsresult rv = NS_NewThread(getter_AddRefs(mPACThread), nullptr);
michael@0 731 if (NS_FAILED(rv))
michael@0 732 return rv;
michael@0 733
michael@0 734 nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &nsPACMan::NamePACThread);
michael@0 735 // don't check return value as it is not a big deal for this to fail.
michael@0 736 mPACThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
michael@0 737
michael@0 738 return NS_OK;
michael@0 739 }
michael@0 740
michael@0 741 namespace mozilla {
michael@0 742 namespace net {
michael@0 743
michael@0 744 PRLogModuleInfo*
michael@0 745 GetProxyLog()
michael@0 746 {
michael@0 747 static PRLogModuleInfo *sLog;
michael@0 748 if (!sLog)
michael@0 749 sLog = PR_NewLogModule("proxy");
michael@0 750 return sLog;
michael@0 751 }
michael@0 752
michael@0 753 }
michael@0 754 }

mercurial