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