Coverage Report

Created: 2018-09-25 14:53

/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