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