Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsProtocolProxyService.cpp
blob63f4048e079f8ed7b295aa6d47d693df97df0b6f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Malcolm Smith <malsmith@cs.rmit.edu.au>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsProtocolProxyService.h"
41 #include "nsProxyInfo.h"
42 #include "nsIClassInfoImpl.h"
43 #include "nsIServiceManager.h"
44 #include "nsXPIDLString.h"
45 #include "nsIProxyAutoConfig.h"
46 #include "nsAutoLock.h"
47 #include "nsIIOService.h"
48 #include "nsIObserverService.h"
49 #include "nsIProtocolHandler.h"
50 #include "nsIProtocolProxyCallback.h"
51 #include "nsICancelable.h"
52 #include "nsIDNSService.h"
53 #include "nsIPrefService.h"
54 #include "nsIPrefBranch2.h"
55 #include "nsReadableUtils.h"
56 #include "nsThreadUtils.h"
57 #include "nsString.h"
58 #include "nsNetUtil.h"
59 #include "nsCRT.h"
60 #include "prnetdb.h"
61 #include "nsPACMan.h"
63 //----------------------------------------------------------------------------
65 #include "prlog.h"
66 #if defined(PR_LOGGING)
67 static PRLogModuleInfo *sLog = PR_NewLogModule("proxy");
68 #endif
69 #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
71 //----------------------------------------------------------------------------
73 #define PROXY_PREF_BRANCH "network.proxy"
74 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
76 #define WPAD_URL "http://wpad/wpad.dat"
78 //----------------------------------------------------------------------------
80 // This structure is intended to be allocated on the stack
81 struct nsProtocolInfo {
82 nsCAutoString scheme;
83 PRUint32 flags;
84 PRInt32 defaultPort;
87 //----------------------------------------------------------------------------
89 class nsAsyncResolveRequest : public nsIRunnable
90 , public nsPACManCallback
91 , public nsICancelable
93 public:
94 NS_DECL_ISUPPORTS
96 nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIURI *uri,
97 nsIProtocolProxyCallback *callback)
98 : mStatus(NS_OK)
99 , mDispatched(PR_FALSE)
100 , mPPS(pps)
101 , mURI(uri)
102 , mCallback(callback)
104 NS_ASSERTION(mCallback, "null callback");
107 void SetResult(nsresult status, nsIProxyInfo *pi)
109 mStatus = status;
110 mProxyInfo = pi;
113 NS_IMETHOD Run()
115 if (mCallback)
116 DoCallback();
117 return NS_OK;
120 NS_IMETHOD Cancel(nsresult reason)
122 NS_ENSURE_ARG(NS_FAILED(reason));
124 // If we've already called DoCallback then, nothing more to do.
125 if (!mCallback)
126 return NS_OK;
128 SetResult(reason, nsnull);
129 return DispatchCallback();
132 nsresult DispatchCallback()
134 if (mDispatched) // Only need to dispatch once
135 return NS_OK;
137 nsresult rv = NS_DispatchToCurrentThread(this);
138 if (NS_FAILED(rv))
139 NS_WARNING("unable to dispatch callback event");
140 else {
141 mDispatched = PR_TRUE;
142 return NS_OK;
145 mCallback = nsnull; // break possible reference cycle
146 return rv;
149 private:
151 // Called asynchronously, so we do not need to post another PLEvent
152 // before calling DoCallback.
153 void OnQueryComplete(nsresult status, const nsCString &pacString)
155 // If we've already called DoCallback then, nothing more to do.
156 if (!mCallback)
157 return;
159 // Provided we haven't been canceled...
160 if (mStatus == NS_OK) {
161 mStatus = status;
162 mPACString = pacString;
165 // In the cancelation case, we may still have another PLEvent in
166 // the queue that wants to call DoCallback. No need to wait for
167 // it, just run the callback now.
168 DoCallback();
171 void DoCallback()
173 // Generate proxy info from the PAC string if appropriate
174 if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty())
175 mPPS->ProcessPACString(mPACString, getter_AddRefs(mProxyInfo));
177 // Now apply proxy filters
178 if (NS_SUCCEEDED(mStatus)) {
179 nsProtocolInfo info;
180 mStatus = mPPS->GetProtocolInfo(mURI, &info);
181 if (NS_SUCCEEDED(mStatus))
182 mPPS->ApplyFilters(mURI, info, mProxyInfo);
183 else
184 mProxyInfo = nsnull;
187 mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus);
188 mCallback = nsnull; // in case the callback holds an owning ref to us
191 private:
193 nsresult mStatus;
194 nsCString mPACString;
195 PRBool mDispatched;
197 nsRefPtr<nsProtocolProxyService> mPPS;
198 nsCOMPtr<nsIURI> mURI;
199 nsCOMPtr<nsIProtocolProxyCallback> mCallback;
200 nsCOMPtr<nsIProxyInfo> mProxyInfo;
203 NS_IMPL_ISUPPORTS2(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
205 //----------------------------------------------------------------------------
207 #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
210 // apply mask to address (zeros out excluded bits).
212 // NOTE: we do the byte swapping here to minimize overall swapping.
214 static void
215 proxy_MaskIPv6Addr(PRIPv6Addr &addr, PRUint16 mask_len)
217 if (mask_len == 128)
218 return;
220 if (mask_len > 96) {
221 addr.pr_s6_addr32[3] = PR_htonl(
222 PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
224 else if (mask_len > 64) {
225 addr.pr_s6_addr32[3] = 0;
226 addr.pr_s6_addr32[2] = PR_htonl(
227 PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
229 else if (mask_len > 32) {
230 addr.pr_s6_addr32[3] = 0;
231 addr.pr_s6_addr32[2] = 0;
232 addr.pr_s6_addr32[1] = PR_htonl(
233 PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
235 else {
236 addr.pr_s6_addr32[3] = 0;
237 addr.pr_s6_addr32[2] = 0;
238 addr.pr_s6_addr32[1] = 0;
239 addr.pr_s6_addr32[0] = PR_htonl(
240 PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
244 static void
245 proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
246 const char *aPref,
247 nsCString &aResult)
249 nsXPIDLCString temp;
250 nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
251 if (NS_FAILED(rv))
252 aResult.Truncate();
253 else {
254 aResult.Assign(temp);
255 // all of our string prefs are hostnames, so we should remove any
256 // whitespace characters that the user might have unknowingly entered.
257 aResult.StripWhitespace();
261 static void
262 proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
263 const char *aPref,
264 PRInt32 &aResult)
266 PRInt32 temp;
267 nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
268 if (NS_FAILED(rv))
269 aResult = -1;
270 else
271 aResult = temp;
274 static void
275 proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
276 const char *aPref,
277 PRBool &aResult)
279 PRBool temp;
280 nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
281 if (NS_FAILED(rv))
282 aResult = PR_FALSE;
283 else
284 aResult = temp;
287 //----------------------------------------------------------------------------
289 NS_IMPL_ADDREF(nsProtocolProxyService)
290 NS_IMPL_RELEASE(nsProtocolProxyService)
291 NS_IMPL_QUERY_INTERFACE3_CI(nsProtocolProxyService,
292 nsIProtocolProxyService,
293 nsIProtocolProxyService2,
294 nsIObserver)
295 NS_IMPL_CI_INTERFACE_GETTER2(nsProtocolProxyService,
296 nsIProtocolProxyService,
297 nsIProtocolProxyService2)
299 nsProtocolProxyService::nsProtocolProxyService()
300 : mFilters(nsnull)
301 , mProxyConfig(eProxyConfig_Direct)
302 , mHTTPProxyPort(-1)
303 , mFTPProxyPort(-1)
304 , mGopherProxyPort(-1)
305 , mHTTPSProxyPort(-1)
306 , mSOCKSProxyPort(-1)
307 , mSOCKSProxyVersion(4)
308 , mSOCKSProxyRemoteDNS(PR_FALSE)
309 , mPACMan(nsnull)
310 , mSessionStart(PR_Now())
311 , mFailedProxyTimeout(30 * 60) // 30 minute default
315 nsProtocolProxyService::~nsProtocolProxyService()
317 // These should have been cleaned up in our Observe method.
318 NS_ASSERTION(mHostFiltersArray.Count() == 0 && mFilters == nsnull &&
319 mPACMan == nsnull, "what happened to xpcom-shutdown?");
322 // nsProtocolProxyService methods
323 nsresult
324 nsProtocolProxyService::Init()
326 if (!mFailedProxies.Init())
327 return NS_ERROR_OUT_OF_MEMORY;
329 // failure to access prefs is non-fatal
330 nsCOMPtr<nsIPrefBranch2> prefBranch =
331 do_GetService(NS_PREFSERVICE_CONTRACTID);
332 if (prefBranch) {
333 // monitor proxy prefs
334 prefBranch->AddObserver(PROXY_PREF_BRANCH, this, PR_FALSE);
336 // read all prefs
337 PrefsChanged(prefBranch, nsnull);
340 // register for shutdown notification so we can clean ourselves up properly.
341 nsCOMPtr<nsIObserverService> obs =
342 do_GetService("@mozilla.org/observer-service;1");
343 if (obs)
344 obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
346 return NS_OK;
349 NS_IMETHODIMP
350 nsProtocolProxyService::Observe(nsISupports *aSubject,
351 const char *aTopic,
352 const PRUnichar *aData)
354 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
355 // cleanup
356 if (mHostFiltersArray.Count() > 0) {
357 mHostFiltersArray.EnumerateForwards(CleanupFilterArray, nsnull);
358 mHostFiltersArray.Clear();
360 if (mFilters) {
361 delete mFilters;
362 mFilters = nsnull;
364 if (mPACMan) {
365 mPACMan->Shutdown();
366 mPACMan = nsnull;
369 else {
370 NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
371 "what is this random observer event?");
372 nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
373 if (prefs)
374 PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
376 return NS_OK;
379 void
380 nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
381 const char *pref)
383 nsresult rv = NS_OK;
384 PRBool reloadPAC = PR_FALSE;
385 nsXPIDLCString tempString;
387 if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
388 PRInt32 type = -1;
389 rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
390 if (NS_SUCCEEDED(rv)) {
391 // bug 115720 - for ns4.x backwards compatability
392 if (type == eProxyConfig_Direct4x) {
393 type = eProxyConfig_Direct;
394 // Reset the type so that the dialog looks correct, and we
395 // don't have to handle this case everywhere else
396 // I'm paranoid about a loop of some sort - only do this
397 // if we're enumerating all prefs, and ignore any error
398 if (!pref)
399 prefBranch->SetIntPref(PROXY_PREF("type"), type);
400 } else if (type >= eProxyConfig_Last) {
401 LOG(("unknown proxy type: %lu; assuming direct\n", type));
402 type = eProxyConfig_Direct;
404 mProxyConfig = static_cast<ProxyConfig>(type);
405 reloadPAC = PR_TRUE;
408 if (mProxyConfig == eProxyConfig_System) {
409 mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
410 } else {
411 mSystemProxySettings = nsnull;
415 if (!pref || !strcmp(pref, PROXY_PREF("http")))
416 proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
418 if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
419 proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
421 if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
422 proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
424 if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
425 proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
427 if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
428 proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
430 if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
431 proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
433 if (!pref || !strcmp(pref, PROXY_PREF("gopher")))
434 proxy_GetStringPref(prefBranch, PROXY_PREF("gopher"), mGopherProxyHost);
436 if (!pref || !strcmp(pref, PROXY_PREF("gopher_port")))
437 proxy_GetIntPref(prefBranch, PROXY_PREF("gopher_port"), mGopherProxyPort);
439 if (!pref || !strcmp(pref, PROXY_PREF("socks")))
440 proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost);
442 if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
443 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
445 if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
446 PRInt32 version;
447 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
448 // make sure this preference value remains sane
449 if (version == 5)
450 mSOCKSProxyVersion = 5;
451 else
452 mSOCKSProxyVersion = 4;
455 if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
456 proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
457 mSOCKSProxyRemoteDNS);
459 if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
460 proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
461 mFailedProxyTimeout);
463 if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
464 rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
465 getter_Copies(tempString));
466 if (NS_SUCCEEDED(rv))
467 LoadHostFilters(tempString.get());
470 // We're done if not using something that could give us a PAC URL
471 // (PAC, WPAD or System)
472 if (mProxyConfig != eProxyConfig_PAC && mProxyConfig != eProxyConfig_WPAD &&
473 mProxyConfig != eProxyConfig_System)
474 return;
476 // OK, we need to reload the PAC file if:
477 // 1) network.proxy.type changed, or
478 // 2) network.proxy.autoconfig_url changed and PAC is configured
480 if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url")))
481 reloadPAC = PR_TRUE;
483 if (reloadPAC) {
484 tempString.Truncate();
485 if (mProxyConfig == eProxyConfig_PAC) {
486 prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"),
487 getter_Copies(tempString));
488 } else if (mProxyConfig == eProxyConfig_WPAD) {
489 // We diverge from the WPAD spec here in that we don't walk the
490 // hosts's FQDN, stripping components until we hit a TLD. Doing so
491 // is dangerous in the face of an incomplete list of TLDs, and TLDs
492 // get added over time. We could consider doing only a single
493 // substitution of the first component, if that proves to help
494 // compatibility.
495 tempString.AssignLiteral(WPAD_URL);
496 } else if (mSystemProxySettings) {
497 // Get System Proxy settings if available
498 mSystemProxySettings->GetPACURI(tempString);
500 if (!tempString.IsEmpty())
501 ConfigureFromPAC(tempString, PR_FALSE);
505 PRBool
506 nsProtocolProxyService::CanUseProxy(nsIURI *aURI, PRInt32 defaultPort)
508 if (mHostFiltersArray.Count() == 0)
509 return PR_TRUE;
511 PRInt32 port;
512 nsCAutoString host;
514 nsresult rv = aURI->GetAsciiHost(host);
515 if (NS_FAILED(rv) || host.IsEmpty())
516 return PR_FALSE;
518 rv = aURI->GetPort(&port);
519 if (NS_FAILED(rv))
520 return PR_FALSE;
521 if (port == -1)
522 port = defaultPort;
524 PRNetAddr addr;
525 PRBool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
527 PRIPv6Addr ipv6;
528 if (is_ipaddr) {
529 // convert parsed address to IPv6
530 if (addr.raw.family == PR_AF_INET) {
531 // convert to IPv4-mapped address
532 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
534 else if (addr.raw.family == PR_AF_INET6) {
535 // copy the address
536 memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
538 else {
539 NS_WARNING("unknown address family");
540 return PR_TRUE; // allow proxying
544 PRInt32 index = -1;
545 while (++index < mHostFiltersArray.Count()) {
546 HostInfo *hinfo = (HostInfo *) mHostFiltersArray[index];
548 if (is_ipaddr != hinfo->is_ipaddr)
549 continue;
550 if (hinfo->port && hinfo->port != port)
551 continue;
553 if (is_ipaddr) {
554 // generate masked version of target IPv6 address
555 PRIPv6Addr masked;
556 memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
557 proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
559 // check for a match
560 if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
561 return PR_FALSE; // proxy disallowed
563 else {
564 PRUint32 host_len = host.Length();
565 PRUint32 filter_host_len = hinfo->name.host_len;
567 if (host_len >= filter_host_len) {
569 // compare last |filter_host_len| bytes of target hostname.
571 const char *host_tail = host.get() + host_len - filter_host_len;
572 if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len))
573 return PR_FALSE; // proxy disallowed
577 return PR_TRUE;
580 static const char kProxyType_HTTP[] = "http";
581 static const char kProxyType_PROXY[] = "proxy";
582 static const char kProxyType_SOCKS[] = "socks";
583 static const char kProxyType_SOCKS4[] = "socks4";
584 static const char kProxyType_SOCKS5[] = "socks5";
585 static const char kProxyType_DIRECT[] = "direct";
586 static const char kProxyType_UNKNOWN[] = "unknown";
588 const char *
589 nsProtocolProxyService::ExtractProxyInfo(const char *start, nsProxyInfo **result)
591 *result = nsnull;
592 PRUint32 flags = 0;
594 // see BNF in nsIProxyAutoConfig.idl
596 // find end of proxy info delimiter
597 const char *end = start;
598 while (*end && *end != ';') ++end;
600 // find end of proxy type delimiter
601 const char *sp = start;
602 while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
604 PRUint32 len = sp - start;
605 const char *type = nsnull;
606 switch (len) {
607 case 5:
608 if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0)
609 type = kProxyType_HTTP;
610 else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0)
611 type = kProxyType_SOCKS4; // assume v4 for 4x compat
612 break;
613 case 6:
614 if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
615 type = kProxyType_DIRECT;
616 else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
617 type = kProxyType_SOCKS4;
618 else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
619 // map "SOCKS5" to "socks" to match contract-id of registered
620 // SOCKS-v5 socket provider.
621 type = kProxyType_SOCKS;
622 break;
624 if (type) {
625 const char *host = nsnull, *hostEnd = nsnull;
626 PRInt32 port = -1;
628 // If it's a SOCKS5 proxy, do name resolution on the server side.
629 // We could use this with SOCKS4a servers too, but they might not
630 // support it.
631 if (type == kProxyType_SOCKS)
632 flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
634 // extract host:port
635 start = sp;
636 while ((*start == ' ' || *start == '\t') && start < end)
637 start++;
638 if (start < end) {
639 host = start;
640 hostEnd = strchr(host, ':');
641 if (!hostEnd || hostEnd > end) {
642 hostEnd = end;
643 // no port, so assume default
644 if (type == kProxyType_HTTP)
645 port = 80;
646 else
647 port = 1080;
649 else
650 port = atoi(hostEnd + 1);
652 nsProxyInfo *pi = new nsProxyInfo;
653 if (pi) {
654 pi->mType = type;
655 pi->mFlags = flags;
656 pi->mTimeout = mFailedProxyTimeout;
657 // YES, it is ok to specify a null proxy host.
658 if (host) {
659 pi->mHost.Assign(host, hostEnd - host);
660 pi->mPort = port;
662 NS_ADDREF(*result = pi);
666 while (*end == ';' || *end == ' ' || *end == '\t')
667 ++end;
668 return end;
671 void
672 nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
674 key.AssignASCII(pi->mType);
675 if (!pi->mHost.IsEmpty()) {
676 key.Append(' ');
677 key.Append(pi->mHost);
678 key.Append(':');
679 key.AppendInt(pi->mPort);
683 PRUint32
684 nsProtocolProxyService::SecondsSinceSessionStart()
686 PRTime now = PR_Now();
688 // get time elapsed since session start
689 PRInt64 diff;
690 LL_SUB(diff, now, mSessionStart);
692 // convert microseconds to seconds
693 PRTime ups;
694 LL_I2L(ups, PR_USEC_PER_SEC);
695 LL_DIV(diff, diff, ups);
697 // convert to 32 bit value
698 PRUint32 dsec;
699 LL_L2UI(dsec, diff);
701 return dsec;
704 void
705 nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
707 nsCAutoString key;
708 GetProxyKey(pi, key);
709 mFailedProxies.Remove(key);
712 void
713 nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
715 nsCAutoString key;
716 GetProxyKey(pi, key);
718 PRUint32 dsec = SecondsSinceSessionStart();
720 // Add timeout to interval (this is the time when the proxy can
721 // be tried again).
722 dsec += pi->mTimeout;
724 // NOTE: The classic codebase would increase the timeout value
725 // incrementally each time a subsequent failure occured.
726 // We could do the same, but it would require that we not
727 // remove proxy entries in IsProxyDisabled or otherwise
728 // change the way we are recording disabled proxies.
729 // Simpler is probably better for now, and at least the
730 // user can tune the timeout setting via preferences.
732 LOG(("DisableProxy %s %d\n", key.get(), dsec));
734 // If this fails, oh well... means we don't have enough memory
735 // to remember the failed proxy.
736 mFailedProxies.Put(key, dsec);
739 PRBool
740 nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
742 nsCAutoString key;
743 GetProxyKey(pi, key);
745 PRUint32 val;
746 if (!mFailedProxies.Get(key, &val))
747 return PR_FALSE;
749 PRUint32 dsec = SecondsSinceSessionStart();
751 // if time passed has exceeded interval, then try proxy again.
752 if (dsec > val) {
753 mFailedProxies.Remove(key);
754 return PR_FALSE;
757 return PR_TRUE;
760 nsresult
761 nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
762 PRBool forceReload)
764 if (!mPACMan) {
765 mPACMan = new nsPACMan();
766 if (!mPACMan)
767 return NS_ERROR_OUT_OF_MEMORY;
770 nsCOMPtr<nsIURI> pacURI;
771 nsresult rv = NS_NewURI(getter_AddRefs(pacURI), spec);
772 if (NS_FAILED(rv))
773 return rv;
775 if (mPACMan->IsPACURI(pacURI) && !forceReload)
776 return NS_OK;
778 mFailedProxies.Clear();
780 return mPACMan->LoadPACFromURI(pacURI);
783 void
784 nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
785 nsIProxyInfo **result)
787 if (pacString.IsEmpty()) {
788 *result = nsnull;
789 return;
792 const char *proxies = pacString.get();
794 nsProxyInfo *pi = nsnull, *first = nsnull, *last = nsnull;
795 while (*proxies) {
796 proxies = ExtractProxyInfo(proxies, &pi);
797 if (pi) {
798 if (last) {
799 NS_ASSERTION(last->mNext == nsnull, "leaking nsProxyInfo");
800 last->mNext = pi;
802 else
803 first = pi;
804 last = pi;
807 *result = first;
810 // nsIProtocolProxyService2
811 NS_IMETHODIMP
812 nsProtocolProxyService::ReloadPAC()
814 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
815 if (!prefs)
816 return NS_OK;
818 PRInt32 type;
819 nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
820 if (NS_FAILED(rv))
821 return NS_OK;
823 nsXPIDLCString pacSpec;
824 if (type == eProxyConfig_PAC)
825 prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec));
826 else if (type == eProxyConfig_WPAD)
827 pacSpec.AssignLiteral(WPAD_URL);
829 if (!pacSpec.IsEmpty())
830 ConfigureFromPAC(pacSpec, PR_TRUE);
831 return NS_OK;
834 // nsIProtocolProxyService
835 NS_IMETHODIMP
836 nsProtocolProxyService::Resolve(nsIURI *uri, PRUint32 flags,
837 nsIProxyInfo **result)
839 nsProtocolInfo info;
840 nsresult rv = GetProtocolInfo(uri, &info);
841 if (NS_FAILED(rv))
842 return rv;
844 PRBool usePAC;
845 rv = Resolve_Internal(uri, info, &usePAC, result);
846 if (NS_FAILED(rv))
847 return rv;
849 if (usePAC && mPACMan) {
850 NS_ASSERTION(*result == nsnull, "we should not have a result yet");
852 // If the caller didn't want us to invoke PAC, then error out.
853 if (flags & RESOLVE_NON_BLOCKING)
854 return NS_BASE_STREAM_WOULD_BLOCK;
856 // Query the PAC file synchronously.
857 nsCString pacString;
858 rv = mPACMan->GetProxyForURI(uri, pacString);
859 if (NS_SUCCEEDED(rv))
860 ProcessPACString(pacString, result);
861 else if (rv == NS_ERROR_IN_PROGRESS) {
862 // Construct a special UNKNOWN proxy entry that informs the caller
863 // that the proxy info is yet to be determined.
864 rv = NewProxyInfo_Internal(kProxyType_UNKNOWN, EmptyCString(), -1,
865 0, 0, nsnull, result);
866 if (NS_FAILED(rv))
867 return rv;
869 else
870 NS_WARNING("failed querying PAC file; trying DIRECT");
873 ApplyFilters(uri, info, result);
874 return NS_OK;
877 NS_IMETHODIMP
878 nsProtocolProxyService::AsyncResolve(nsIURI *uri, PRUint32 flags,
879 nsIProtocolProxyCallback *callback,
880 nsICancelable **result)
882 nsRefPtr<nsAsyncResolveRequest> ctx =
883 new nsAsyncResolveRequest(this, uri, callback);
884 if (!ctx)
885 return NS_ERROR_OUT_OF_MEMORY;
887 nsProtocolInfo info;
888 nsresult rv = GetProtocolInfo(uri, &info);
889 if (NS_FAILED(rv))
890 return rv;
892 PRBool usePAC;
893 nsCOMPtr<nsIProxyInfo> pi;
894 rv = Resolve_Internal(uri, info, &usePAC, getter_AddRefs(pi));
895 if (NS_FAILED(rv))
896 return rv;
898 if (!usePAC || !mPACMan) {
899 ApplyFilters(uri, info, pi);
901 ctx->SetResult(NS_OK, pi);
902 return ctx->DispatchCallback();
905 // else kick off a PAC query
906 rv = mPACMan->AsyncGetProxyForURI(uri, ctx);
907 if (NS_SUCCEEDED(rv)) {
908 *result = ctx;
909 NS_ADDREF(*result);
911 return rv;
914 NS_IMETHODIMP
915 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
916 const nsACString &aHost,
917 PRInt32 aPort,
918 PRUint32 aFlags,
919 PRUint32 aFailoverTimeout,
920 nsIProxyInfo *aFailoverProxy,
921 nsIProxyInfo **aResult)
923 static const char *types[] = {
924 kProxyType_HTTP,
925 kProxyType_SOCKS,
926 kProxyType_SOCKS4,
927 kProxyType_DIRECT
930 // resolve type; this allows us to avoid copying the type string into each
931 // proxy info instance. we just reference the string literals directly :)
932 const char *type = nsnull;
933 for (PRUint32 i=0; i<NS_ARRAY_LENGTH(types); ++i) {
934 if (aType.LowerCaseEqualsASCII(types[i])) {
935 type = types[i];
936 break;
939 NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
941 if (aPort <= 0)
942 aPort = -1;
944 return NewProxyInfo_Internal(type, aHost, aPort, aFlags, aFailoverTimeout,
945 aFailoverProxy, aResult);
948 NS_IMETHODIMP
949 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo *aProxy,
950 nsIURI *aURI,
951 nsresult aStatus,
952 nsIProxyInfo **aResult)
954 // We only support failover when a PAC file is configured, either
955 // directly or via system settings
956 if (mProxyConfig != eProxyConfig_PAC && mProxyConfig != eProxyConfig_WPAD &&
957 mProxyConfig != eProxyConfig_System)
958 return NS_ERROR_NOT_AVAILABLE;
960 // Verify that |aProxy| is one of our nsProxyInfo objects.
961 nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
962 NS_ENSURE_ARG(pi);
963 // OK, the QI checked out. We can proceed.
965 // Remember that this proxy is down.
966 DisableProxy(pi);
968 // NOTE: At this point, we might want to prompt the user if we have
969 // not already tried going DIRECT. This is something that the
970 // classic codebase supported; however, IE6 does not prompt.
972 if (!pi->mNext)
973 return NS_ERROR_NOT_AVAILABLE;
975 LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
976 pi->mType, pi->mHost.get(), pi->mPort,
977 pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
979 NS_ADDREF(*aResult = pi->mNext);
980 return NS_OK;
983 NS_IMETHODIMP
984 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
985 PRUint32 position)
987 UnregisterFilter(filter); // remove this filter if we already have it
989 FilterLink *link = new FilterLink(position, filter);
990 if (!link)
991 return NS_ERROR_OUT_OF_MEMORY;
993 if (!mFilters) {
994 mFilters = link;
995 return NS_OK;
998 // insert into mFilters in sorted order
999 FilterLink *last = nsnull;
1000 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1001 if (position < iter->position) {
1002 if (last) {
1003 link->next = last->next;
1004 last->next = link;
1006 else {
1007 link->next = mFilters;
1008 mFilters = link;
1010 return NS_OK;
1012 last = iter;
1014 // our position is equal to or greater than the last link in the list
1015 last->next = link;
1016 return NS_OK;
1019 NS_IMETHODIMP
1020 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter)
1022 // QI to nsISupports so we can safely test object identity.
1023 nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
1025 FilterLink *last = nsnull;
1026 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1027 nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
1028 if (object == givenObject) {
1029 if (last)
1030 last->next = iter->next;
1031 else
1032 mFilters = iter->next;
1033 iter->next = nsnull;
1034 delete iter;
1035 return NS_OK;
1037 last = iter;
1040 // No need to throw an exception in this case.
1041 return NS_OK;
1044 PRBool
1045 nsProtocolProxyService::CleanupFilterArray(void *aElement, void *aData)
1047 if (aElement)
1048 delete (HostInfo *) aElement;
1050 return PR_TRUE;
1053 void
1054 nsProtocolProxyService::LoadHostFilters(const char *filters)
1056 // check to see the owners flag? /!?/ TODO
1057 if (mHostFiltersArray.Count() > 0) {
1058 mHostFiltersArray.EnumerateForwards(CleanupFilterArray, nsnull);
1059 mHostFiltersArray.Clear();
1062 if (!filters)
1063 return; // fail silently...
1066 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1067 // filters = filter *( "," LWS filter)
1069 while (*filters) {
1070 // skip over spaces and ,
1071 while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters)))
1072 filters++;
1074 const char *starthost = filters;
1075 const char *endhost = filters + 1; // at least that...
1076 const char *portLocation = 0;
1077 const char *maskLocation = 0;
1080 // XXX this needs to be fixed to support IPv6 address literals,
1081 // which in this context will need to be []-escaped.
1083 while (*endhost && (*endhost != ',' && !IS_ASCII_SPACE(*endhost))) {
1084 if (*endhost == ':')
1085 portLocation = endhost;
1086 else if (*endhost == '/')
1087 maskLocation = endhost;
1088 endhost++;
1091 filters = endhost; // advance iterator up front
1093 HostInfo *hinfo = new HostInfo();
1094 if (!hinfo)
1095 return; // fail silently
1096 hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
1098 // locate end of host
1099 const char *end = maskLocation ? maskLocation :
1100 portLocation ? portLocation :
1101 endhost;
1103 nsCAutoString str(starthost, end - starthost);
1105 PRNetAddr addr;
1106 if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) {
1107 hinfo->is_ipaddr = PR_TRUE;
1108 hinfo->ip.family = PR_AF_INET6; // we always store address as IPv6
1109 hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128;
1111 if (hinfo->ip.mask_len == 0) {
1112 NS_WARNING("invalid mask");
1113 goto loser;
1116 if (addr.raw.family == PR_AF_INET) {
1117 // convert to IPv4-mapped address
1118 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
1119 // adjust mask_len accordingly
1120 if (hinfo->ip.mask_len <= 32)
1121 hinfo->ip.mask_len += 96;
1123 else if (addr.raw.family == PR_AF_INET6) {
1124 // copy the address
1125 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1127 else {
1128 NS_WARNING("unknown address family");
1129 goto loser;
1132 // apply mask to IPv6 address
1133 proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1135 else {
1136 PRUint32 startIndex, endIndex;
1137 if (str.First() == '*')
1138 startIndex = 1; // *.domain -> .domain
1139 else
1140 startIndex = 0;
1141 endIndex = (portLocation ? portLocation : endhost) - starthost;
1143 hinfo->is_ipaddr = PR_FALSE;
1144 hinfo->name.host = ToNewCString(Substring(str, startIndex, endIndex));
1146 if (!hinfo->name.host)
1147 goto loser;
1149 hinfo->name.host_len = endIndex - startIndex;
1152 //#define DEBUG_DUMP_FILTERS
1153 #ifdef DEBUG_DUMP_FILTERS
1154 printf("loaded filter[%u]:\n", mHostFiltersArray.Count());
1155 printf(" is_ipaddr = %u\n", hinfo->is_ipaddr);
1156 printf(" port = %u\n", hinfo->port);
1157 if (hinfo->is_ipaddr) {
1158 printf(" ip.family = %x\n", hinfo->ip.family);
1159 printf(" ip.mask_len = %u\n", hinfo->ip.mask_len);
1161 PRNetAddr netAddr;
1162 PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
1163 memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
1165 char buf[256];
1166 PR_NetAddrToString(&netAddr, buf, sizeof(buf));
1168 printf(" ip.addr = %s\n", buf);
1170 else {
1171 printf(" name.host = %s\n", hinfo->name.host);
1173 #endif
1175 mHostFiltersArray.AppendElement(hinfo);
1176 hinfo = nsnull;
1177 loser:
1178 if (hinfo)
1179 delete hinfo;
1183 nsresult
1184 nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
1186 nsresult rv;
1188 rv = uri->GetScheme(info->scheme);
1189 if (NS_FAILED(rv))
1190 return rv;
1192 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
1193 if (NS_FAILED(rv))
1194 return rv;
1196 nsCOMPtr<nsIProtocolHandler> handler;
1197 rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
1198 if (NS_FAILED(rv))
1199 return rv;
1201 rv = handler->GetProtocolFlags(&info->flags);
1202 if (NS_FAILED(rv))
1203 return rv;
1205 rv = handler->GetDefaultPort(&info->defaultPort);
1206 return rv;
1209 nsresult
1210 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
1211 const nsACString &aHost,
1212 PRInt32 aPort,
1213 PRUint32 aFlags,
1214 PRUint32 aFailoverTimeout,
1215 nsIProxyInfo *aFailoverProxy,
1216 nsIProxyInfo **aResult)
1218 nsCOMPtr<nsProxyInfo> failover;
1219 if (aFailoverProxy) {
1220 failover = do_QueryInterface(aFailoverProxy);
1221 NS_ENSURE_ARG(failover);
1224 nsProxyInfo *proxyInfo = new nsProxyInfo();
1225 if (!proxyInfo)
1226 return NS_ERROR_OUT_OF_MEMORY;
1228 proxyInfo->mType = aType;
1229 proxyInfo->mHost = aHost;
1230 proxyInfo->mPort = aPort;
1231 proxyInfo->mFlags = aFlags;
1232 proxyInfo->mTimeout = aFailoverTimeout == PR_UINT32_MAX
1233 ? mFailedProxyTimeout : aFailoverTimeout;
1234 failover.swap(proxyInfo->mNext);
1236 NS_ADDREF(*aResult = proxyInfo);
1237 return NS_OK;
1240 nsresult
1241 nsProtocolProxyService::Resolve_Internal(nsIURI *uri,
1242 const nsProtocolInfo &info,
1243 PRBool *usePAC,
1244 nsIProxyInfo **result)
1246 NS_ENSURE_ARG_POINTER(uri);
1248 *usePAC = PR_FALSE;
1249 *result = nsnull;
1251 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1252 return NS_OK; // Can't proxy this (filters may not override)
1254 if (mSystemProxySettings) {
1255 nsCAutoString PACURI;
1256 if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
1257 !PACURI.IsEmpty()) {
1258 // Switch to new PAC file if that setting has changed. If the setting
1259 // hasn't changed, ConfigureFromPAC will exit early.
1260 nsresult rv = ConfigureFromPAC(PACURI, PR_FALSE);
1261 if (NS_FAILED(rv))
1262 return rv;
1263 } else {
1264 nsCAutoString proxy;
1265 nsresult rv = mSystemProxySettings->GetProxyForURI(uri, proxy);
1266 if (NS_SUCCEEDED(rv)) {
1267 ProcessPACString(proxy, result);
1268 return NS_OK;
1270 // no proxy, stop search
1271 return NS_OK;
1275 // if proxies are enabled and this host:port combo is supposed to use a
1276 // proxy, check for a proxy.
1277 if (mProxyConfig == eProxyConfig_Direct ||
1278 (mProxyConfig == eProxyConfig_Manual &&
1279 !CanUseProxy(uri, info.defaultPort)))
1280 return NS_OK;
1282 // Proxy auto config magic...
1283 if (mProxyConfig == eProxyConfig_PAC || mProxyConfig == eProxyConfig_WPAD ||
1284 mProxyConfig == eProxyConfig_System) {
1285 // Do not query PAC now.
1286 *usePAC = PR_TRUE;
1287 return NS_OK;
1290 // proxy info values
1291 const char *type = nsnull;
1292 const nsACString *host = nsnull;
1293 PRInt32 port = -1;
1295 PRUint32 proxyFlags = 0;
1297 if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
1298 info.scheme.EqualsLiteral("http")) {
1299 host = &mHTTPProxyHost;
1300 type = kProxyType_HTTP;
1301 port = mHTTPProxyPort;
1303 else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
1304 info.scheme.EqualsLiteral("https")) {
1305 host = &mHTTPSProxyHost;
1306 type = kProxyType_HTTP;
1307 port = mHTTPSProxyPort;
1309 else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
1310 info.scheme.EqualsLiteral("ftp")) {
1311 host = &mFTPProxyHost;
1312 type = kProxyType_HTTP;
1313 port = mFTPProxyPort;
1315 else if (!mGopherProxyHost.IsEmpty() && mGopherProxyPort > 0 &&
1316 info.scheme.EqualsLiteral("gopher")) {
1317 host = &mGopherProxyHost;
1318 type = kProxyType_HTTP;
1319 port = mGopherProxyPort;
1321 else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
1322 host = &mSOCKSProxyHost;
1323 if (mSOCKSProxyVersion == 4)
1324 type = kProxyType_SOCKS4;
1325 else
1326 type = kProxyType_SOCKS;
1327 port = mSOCKSProxyPort;
1328 if (mSOCKSProxyRemoteDNS)
1329 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1332 if (type) {
1333 nsresult rv = NewProxyInfo_Internal(type, *host, port, proxyFlags,
1334 PR_UINT32_MAX, nsnull, result);
1335 if (NS_FAILED(rv))
1336 return rv;
1339 return NS_OK;
1342 void
1343 nsProtocolProxyService::ApplyFilters(nsIURI *uri, const nsProtocolInfo &info,
1344 nsIProxyInfo **list)
1346 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1347 return;
1349 // We prune the proxy list prior to invoking each filter. This may be
1350 // somewhat inefficient, but it seems like a good idea since we want each
1351 // filter to "see" a valid proxy list.
1353 nsresult rv;
1354 nsCOMPtr<nsIProxyInfo> result;
1356 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1357 PruneProxyInfo(info, list);
1359 rv = iter->filter->ApplyFilter(this, uri, *list,
1360 getter_AddRefs(result));
1361 if (NS_FAILED(rv))
1362 continue;
1363 result.swap(*list);
1366 PruneProxyInfo(info, list);
1369 void
1370 nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
1371 nsIProxyInfo **list)
1373 if (!*list)
1374 return;
1375 nsProxyInfo *head = nsnull;
1376 CallQueryInterface(*list, &head);
1377 if (!head) {
1378 NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
1379 return;
1381 NS_RELEASE(*list);
1383 // Pruning of disabled proxies works like this:
1384 // - If all proxies are disabled, return the full list
1385 // - Otherwise, remove the disabled proxies.
1387 // Pruning of disallowed proxies works like this:
1388 // - If the protocol handler disallows the proxy, then we disallow it.
1390 // Start by removing all disallowed proxies if required:
1391 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
1392 nsProxyInfo *last = nsnull, *iter = head;
1393 while (iter) {
1394 if (iter->Type() == kProxyType_HTTP) {
1395 // reject!
1396 if (last)
1397 last->mNext = iter->mNext;
1398 else
1399 head = iter->mNext;
1400 nsProxyInfo *next = iter->mNext;
1401 iter->mNext = nsnull;
1402 iter->Release();
1403 iter = next;
1404 } else {
1405 last = iter;
1406 iter = iter->mNext;
1409 if (!head)
1410 return;
1413 // Now, scan to see if all remaining proxies are disabled. If so, then
1414 // we'll just bail and return them all. Otherwise, we'll go and prune the
1415 // disabled ones.
1417 PRBool allDisabled = PR_TRUE;
1419 nsProxyInfo *iter;
1420 for (iter = head; iter; iter = iter->mNext) {
1421 if (!IsProxyDisabled(iter)) {
1422 allDisabled = PR_FALSE;
1423 break;
1427 if (allDisabled)
1428 LOG(("All proxies are disabled, so trying all again"));
1429 else {
1430 // remove any disabled proxies.
1431 nsProxyInfo *last = nsnull;
1432 for (iter = head; iter; ) {
1433 if (IsProxyDisabled(iter)) {
1434 // reject!
1435 nsProxyInfo *reject = iter;
1437 iter = iter->mNext;
1438 if (last)
1439 last->mNext = iter;
1440 else
1441 head = iter;
1443 reject->mNext = nsnull;
1444 NS_RELEASE(reject);
1445 continue;
1448 // since we are about to use this proxy, make sure it is not on
1449 // the disabled proxy list. we'll add it back to that list if
1450 // we have to (in GetFailoverForProxy).
1452 // XXX(darin): It might be better to do this as a final pass.
1454 EnableProxy(iter);
1456 last = iter;
1457 iter = iter->mNext;
1461 // if only DIRECT was specified then return no proxy info, and we're done.
1462 if (head && !head->mNext && head->mType == kProxyType_DIRECT)
1463 NS_RELEASE(head);
1465 *list = head; // Transfer ownership