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