Source code

Revision control

Other Tools

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