From 7e1bdbfb809c69bc52412005a375225d2ed09fc7 Mon Sep 17 00:00:00 2001 From: "darin%meer.net" Date: Thu, 1 Jul 2004 23:31:47 +0000 Subject: [PATCH] fixes bug 224237 "PAC: make failover behave according to specification" original patch by jerry.tan@sun.com with revisions by darin, r=biesi git-svn-id: svn://10.0.0.236/trunk@158673 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/modules/libpref/src/init/all.js | 1 + .../base/public/nsIProtocolProxyService.idl | 90 +++- mozilla/netwerk/base/public/nsIProxyInfo.idl | 20 +- mozilla/netwerk/base/public/nsNetUtil.h | 8 +- mozilla/netwerk/base/src/nsIOService.h | 39 +- .../base/src/nsProtocolProxyService.cpp | 441 ++++++++++++++---- .../netwerk/base/src/nsProtocolProxyService.h | 104 ++--- .../protocol/http/src/nsHttpChannel.cpp | 46 +- 8 files changed, 531 insertions(+), 218 deletions(-) diff --git a/mozilla/modules/libpref/src/init/all.js b/mozilla/modules/libpref/src/init/all.js index 058296ac792..28802ac4098 100644 --- a/mozilla/modules/libpref/src/init/all.js +++ b/mozilla/modules/libpref/src/init/all.js @@ -598,6 +598,7 @@ pref("network.proxy.socks", ""); pref("network.proxy.socks_port", 0); pref("network.proxy.socks_version", 5); pref("network.proxy.no_proxies_on", "localhost, 127.0.0.1"); +pref("network.proxy.failover_timeout", 1800); // 30 minutes pref("network.online", true); //online/offline pref("network.cookie.cookieBehavior", 3); // 0-Accept, 1-dontAcceptForeign, 2-dontUse, 3-p3p pref("network.cookie.disableCookieForMailNews", true); // disable all cookies for mail diff --git a/mozilla/netwerk/base/public/nsIProtocolProxyService.idl b/mozilla/netwerk/base/public/nsIProtocolProxyService.idl index 29a08477ad4..bba0b0046c5 100644 --- a/mozilla/netwerk/base/public/nsIProtocolProxyService.idl +++ b/mozilla/netwerk/base/public/nsIProtocolProxyService.idl @@ -1,4 +1,5 @@ /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -20,6 +21,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Darin Fisher * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -36,20 +38,96 @@ * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" -#include "nsIURI.idl" interface nsIProxyInfo; +interface nsIChannel; +interface nsIURI; -[scriptable, uuid(91c4fe40-76c2-433f-9324-ffdae12df4b4)] +/** + * nsIProtocolProxyService provides methods to access information about + * various network proxies. + */ +[scriptable, uuid(6b40d918-c01a-42a2-ab4d-4ea0b707f91f)] interface nsIProtocolProxyService : nsISupports { + /** + * This attribute indicates whether or not support for proxies is enabled. + */ readonly attribute PRBool proxyEnabled; - /** Given a uri, return a proxyInfo */ + /** + * This method returns a nsIProxyInfo instance that identifies a proxy to + * be used for loading the given URL. Otherwise, this method returns null + * indicating that a direct connection should be used. + * + * @param aURI + * The URI to test. + * + * NOTE: If this proxy is unavailable, getFailoverForProxy may be called + * to determine the correct secondary proxy to be used. + * + * NOTE: If the protocol handler for the given URI supports + * nsIProxiedProtocolHandler, then the nsIProxyInfo instance returned + * from examineForProxy may be passed to the newProxiedChannel method + * to create a nsIChannel to the given URI that uses the specified proxy. + * + * NOTE: However, if the nsIProxyInfo type is "HTTP", then it means that + * the given URI should be loaded using the HTTP protocol handler, which + * also supports nsIProxiedProtocolHandler. + * + * @see nsIProxiedProtocolHandler::newProxiedChannel + */ nsIProxyInfo examineForProxy(in nsIURI aURI); - /** Return a proxyInfo with the given data */ - nsIProxyInfo newProxyInfo(in string type, in string host, in long port); + /** + * This method may be called to construct a nsIProxyInfo instance from + * the given parameters. This method may be useful in conjunction with + * nsISocketTransportService::createTransport for creating, for example, + * a SOCKS connection. + * + * @param aType + * The proxy type. This is a string value that identifies the proxy + * type. Standard values include: + * "HTTP" - specifies a HTTP proxy + * "SOCKS" - specifies a SOCKS version 5 proxy + * "SOCKS4" - specifies a SOCKS version 4 proxy + * The type name is case insensitive. Other string values may be + * possible. + * @param aHost + * The proxy hostname or IP address. + * @param aPort + * The proxy port. + */ + nsIProxyInfo newProxyInfo(in ACString aType, in AUTF8String aHost, in long aPort); - void configureFromPAC(in string url); + /** + * This method may be called to re-configure proxy settings given a URI + * to a new proxy auto config file. This method may return before the + * configuration actually takes affect (i.e., the URI may be loaded + * asynchronously). + * + * @param aURI + * The location of the PAC file to load. + */ + void configureFromPAC(in AUTF8String aURI); + + /** + * If the proxy identified by aProxyInfo is unavailable for some reason, + * this method may be called to access an alternate proxy that may be used + * instead. As a side-effect, this method may affect future return values + * from examineForProxy as well as from getFailoverProxy. + * + * @param aProxyInfo + * The proxy that was unavailable. + * @param aURI + * The URI that was originally passed to ExamineForProxy. + * @param aReason + * The error code corresponding to the proxy failure. This value + * may be used to tune the delay before this proxy is used again. + * + * @throw NS_ERROR_NOT_AVAILABLE if there is no alternate proxy available. + */ + nsIProxyInfo getFailoverForProxy(in nsIProxyInfo aProxyInfo, + in nsIURI aURI, + in nsresult aReason); }; diff --git a/mozilla/netwerk/base/public/nsIProxyInfo.idl b/mozilla/netwerk/base/public/nsIProxyInfo.idl index 7afa34c378e..392f12bbf2d 100644 --- a/mozilla/netwerk/base/public/nsIProxyInfo.idl +++ b/mozilla/netwerk/base/public/nsIProxyInfo.idl @@ -38,26 +38,24 @@ #include "nsISupports.idl" -/* This interface is PRIVATE. +/** + * This interface is PRIVATE. + * * It is used as an opaque cookie, and this class may change at * any time without notice. * - * @see nsIProtocolProxyService::GetProxyInfo + * XXX The HTTP protocol handler does not treat this type as opaque, + * so perhaps it is wrong to restrict it to be opaque. + * + * @see nsIProtocolProxyService::examineForProxy + * @see nsISocketTransportService::createTransport */ - native constCharPtr(const char*); [scriptable, uuid(b65d22b0-1dd1-11b2-8f95-920e5b7b56f0)] interface nsIProxyInfo : nsISupports { [noscript, notxpcom] constCharPtr Host(); - [noscript, notxpcom] PRInt32 Port(); + [noscript, notxpcom] PRInt32 Port(); [noscript, notxpcom] constCharPtr Type(); - - /** - * proxy info objects may be chained if several proxies could be treated - * equivalently. this is used to support proxy failover. - */ - readonly attribute nsIProxyInfo next; }; - diff --git a/mozilla/netwerk/base/public/nsNetUtil.h b/mozilla/netwerk/base/public/nsNetUtil.h index 311906c99de..c88b73dd0ce 100644 --- a/mozilla/netwerk/base/public/nsNetUtil.h +++ b/mozilla/netwerk/base/public/nsNetUtil.h @@ -561,10 +561,10 @@ NS_CheckPortSafety(PRInt32 port, } inline nsresult -NS_NewProxyInfo(const char *type, - const char *host, - PRInt32 port, - nsIProxyInfo **result) +NS_NewProxyInfo(const nsACString &type, + const nsACString &host, + PRInt32 port, + nsIProxyInfo **result) { nsresult rv; static NS_DEFINE_CID(kPPSServiceCID, NS_PROTOCOLPROXYSERVICE_CID); diff --git a/mozilla/netwerk/base/src/nsIOService.h b/mozilla/netwerk/base/src/nsIOService.h index 9d95a2b26de..bdc771245d1 100644 --- a/mozilla/netwerk/base/src/nsIOService.h +++ b/mozilla/netwerk/base/src/nsIOService.h @@ -75,36 +75,29 @@ class nsIOService : public nsIIOService { public: NS_DECL_ISUPPORTS - - // nsIIOService methods: NS_DECL_NSIIOSERVICE - - // nsIObserver methods: NS_DECL_NSIOBSERVER - // nsIOService methods: - nsIOService(); - virtual ~nsIOService(); + nsIOService() NS_HIDDEN; + ~nsIOService() NS_HIDDEN; - static NS_METHOD - Create(nsISupports *aOuter, REFNSIID aIID, void **aResult); - - nsresult Init(); - nsresult NewURI(const char* aSpec, nsIURI* aBaseURI, - nsIURI* *result, nsIProtocolHandler* *hdlrResult); + NS_HIDDEN_(nsresult) Init(); + NS_HIDDEN_(nsresult) NewURI(const char* aSpec, nsIURI* aBaseURI, + nsIURI* *result, + nsIProtocolHandler* *hdlrResult); protected: - nsresult GetCachedProtocolHandler(const char *scheme, - nsIProtocolHandler* *hdlrResult, - PRUint32 start=0, - PRUint32 end=0); - nsresult CacheProtocolHandler(const char *scheme, - nsIProtocolHandler* hdlr); + NS_HIDDEN_(nsresult) GetCachedProtocolHandler(const char *scheme, + nsIProtocolHandler* *hdlrResult, + PRUint32 start=0, + PRUint32 end=0); + NS_HIDDEN_(nsresult) CacheProtocolHandler(const char *scheme, + nsIProtocolHandler* hdlr); // Prefs wrangling - void PrefsChanged(nsIPrefBranch *prefs, const char *pref = nsnull); - void GetPrefBranch(nsIPrefBranchInternal **); - void ParsePortList(nsIPrefBranch *prefBranch, const char *pref, PRBool remove); + NS_HIDDEN_(void) PrefsChanged(nsIPrefBranch *prefs, const char *pref = nsnull); + NS_HIDDEN_(void) GetPrefBranch(nsIPrefBranchInternal **); + NS_HIDDEN_(void) ParsePortList(nsIPrefBranch *prefBranch, const char *pref, PRBool remove); protected: PRPackedBool mOffline; @@ -126,5 +119,3 @@ public: }; #endif // nsIOService_h__ - - diff --git a/mozilla/netwerk/base/src/nsProtocolProxyService.cpp b/mozilla/netwerk/base/src/nsProtocolProxyService.cpp index de927457452..52ac876ff45 100644 --- a/mozilla/netwerk/base/src/nsProtocolProxyService.cpp +++ b/mozilla/netwerk/base/src/nsProtocolProxyService.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -40,8 +41,8 @@ #include "nsXPIDLString.h" #include "nsIProxyAutoConfig.h" #include "nsAutoLock.h" +#include "nsEventQueueUtils.h" #include "nsIIOService.h" -#include "nsIEventQueueService.h" #include "nsIProtocolHandler.h" #include "nsIPrefService.h" #include "nsIPrefBranchInternal.h" @@ -51,6 +52,16 @@ #include "nsCRT.h" #include "prnetdb.h" +//---------------------------------------------------------------------------- + +#include "prlog.h" +#if defined(PR_LOGGING) +static PRLogModuleInfo *sLog = PR_NewLogModule("proxy"); +#endif +#define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args) + +//---------------------------------------------------------------------------- + #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t') // @@ -118,11 +129,72 @@ proxy_GetIntPref(nsIPrefBranch *aPrefBranch, aResult = temp; } +//---------------------------------------------------------------------------- + +class nsProxyInfo : public nsIProxyInfo +{ +public: + NS_DECL_ISUPPORTS + + NS_IMETHOD_(const char*) Host() + { + return mHost.get(); + } + + NS_IMETHOD_(PRInt32) Port() + { + return mPort; + } + + NS_IMETHOD_(const char*) Type() + { + return mType; + } + + ~nsProxyInfo() + { + NS_IF_RELEASE(mNext); + } + + nsProxyInfo(const char *type = nsnull) + : mType(type) + , mPort(-1) + , mNext(nsnull) + {} + + const char *mType; // pointer to static kProxyType_XYZ value + nsCString mHost; + PRInt32 mPort; + nsProxyInfo *mNext; +}; + +#define NS_PROXYINFO_ID \ +{ /* ed42f751-825e-4cc2-abeb-3670711a8b85 */ \ + 0xed42f751, \ + 0x825e, \ + 0x4cc2, \ + {0xab, 0xeb, 0x36, 0x70, 0x71, 0x1a, 0x8b, 0x85} \ +} +static NS_DEFINE_IID(kProxyInfoID, NS_PROXYINFO_ID); + +// These objects will be accessed on the socket transport thread. +NS_IMPL_THREADSAFE_ADDREF(nsProxyInfo) +NS_IMPL_THREADSAFE_RELEASE(nsProxyInfo) + +NS_INTERFACE_MAP_BEGIN(nsProxyInfo) + NS_INTERFACE_MAP_ENTRY(nsIProxyInfo) + NS_INTERFACE_MAP_ENTRY(nsISupports) + // see nsProtocolProxyInfo::GetFailoverForProxy + if (aIID.Equals(kProxyInfoID)) + foundInterface = this; + else +NS_INTERFACE_MAP_END + +//---------------------------------------------------------------------------- + NS_IMPL_THREADSAFE_ISUPPORTS2(nsProtocolProxyService, nsIProtocolProxyService, nsIObserver) -NS_IMPL_THREADSAFE_ISUPPORTS1(nsProtocolProxyService::nsProxyInfo, - nsIProxyInfo) nsProtocolProxyService::nsProtocolProxyService() : mUseProxy(0) @@ -132,6 +204,8 @@ nsProtocolProxyService::nsProtocolProxyService() , mHTTPSProxyPort(-1) , mSOCKSProxyPort(-1) , mSOCKSProxyVersion(4) + , mSessionStart(PR_Now()) + , mFailedProxyTimeout(30 * 60) // 30 minute default { } @@ -147,8 +221,12 @@ nsProtocolProxyService::~nsProtocolProxyService() nsresult nsProtocolProxyService::Init() { + if (!mFailedProxies.Init()) + return NS_ERROR_OUT_OF_MEMORY; + // failure to access prefs is non-fatal - nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID); + nsCOMPtr prefBranch = + do_GetService(NS_PREFSERVICE_CONTRACTID); if (prefBranch) { // monitor proxy prefs prefBranch->AddObserver("network.proxy", this, PR_FALSE); @@ -156,6 +234,7 @@ nsProtocolProxyService::Init() // read all prefs PrefsChanged(prefBranch, nsnull); } + return NS_OK; } @@ -238,6 +317,9 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch, mSOCKSProxyVersion = 4; } + if (!pref || !strcmp(pref, "network.proxy.failover_timeout")) + proxy_GetIntPref(prefBranch, "network.proxy.failover_timeout", mFailedProxyTimeout); + if (!pref || !strcmp(pref, "network.proxy.no_proxies_on")) { rv = prefBranch->GetCharPref("network.proxy.no_proxies_on", getter_Copies(tempString)); @@ -248,7 +330,7 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch, if ((!pref || !strcmp(pref, "network.proxy.autoconfig_url") || reloadPAC) && (mUseProxy == 2)) { rv = prefBranch->GetCharPref("network.proxy.autoconfig_url", getter_Copies(tempString)); - if (NS_SUCCEEDED(rv) && (!reloadPAC || strcmp(tempString.get(), mPACURL.get()))) + if (NS_SUCCEEDED(rv) && (!reloadPAC || strcmp(tempString.get(), mPACURI.get()))) ConfigureFromPAC(tempString); } } @@ -257,47 +339,47 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch, void* PR_CALLBACK nsProtocolProxyService::HandlePACLoadEvent(PLEvent* aEvent) { - nsresult rv = NS_OK; - nsProtocolProxyService *pps = (nsProtocolProxyService *) PL_GetEventOwner(aEvent); if (!pps) { NS_ERROR("HandlePACLoadEvent owner is null"); - return NULL; + return nsnull; } + nsresult rv; + // create pac js component pps->mPAC = do_CreateInstance(NS_PROXYAUTOCONFIG_CONTRACTID, &rv); if (!pps->mPAC || NS_FAILED(rv)) { NS_ERROR("Cannot load PAC js component"); - return NULL; + return nsnull; } - if (pps->mPACURL.IsEmpty()) { + if (pps->mPACURI.IsEmpty()) { NS_ERROR("HandlePACLoadEvent: js PACURL component is empty"); - return NULL; + return nsnull; } - nsCOMPtr pIOService(do_GetIOService(&rv)); - if (!pIOService || NS_FAILED(rv)) { - NS_ERROR("Cannot get IO Service"); - return NULL; + nsCOMPtr ios = do_GetIOService(&rv); + if (NS_FAILED(rv)) { + NS_ERROR("No IO service"); + return nsnull; } nsCOMPtr pURI; - rv = pIOService->NewURI(pps->mPACURL, nsnull, nsnull, getter_AddRefs(pURI)); + rv = ios->NewURI(pps->mPACURI, nsnull, nsnull, getter_AddRefs(pURI)); if (NS_FAILED(rv)) { NS_ERROR("New URI failed"); - return NULL; + return nsnull; } - rv = pps->mPAC->LoadPACFromURI(pURI, pIOService); + rv = pps->mPAC->LoadPACFromURI(pURI, ios); if (NS_FAILED(rv)) { NS_ERROR("Load PAC failed"); - return NULL; + return nsnull; } - return NULL; + return nsnull; } void PR_CALLBACK @@ -396,8 +478,6 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, PRBool permitHttp, n { // see BNF in nsIProxyAutoConfig.idl - *result = nsnull; - // find end of proxy info delimiter const char *end = start; while (*end && *end != ';') ++end; @@ -452,7 +532,7 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, PRBool permitHttp, n pi->mType = type; // YES, it is ok to specify a null proxy host. if (host) { - pi->mHost = PL_strndup(host, hostEnd - host); + pi->mHost.Assign(host, hostEnd - host); pi->mPort = port; } NS_ADDREF(*result = pi); @@ -464,6 +544,179 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, PRBool permitHttp, n return end; } +void +nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key) +{ + key.AssignASCII(pi->mType); + if (!pi->mHost.IsEmpty()) { + key.Append(' '); + key.Append(pi->mHost); + key.Append(':'); + key.AppendInt(pi->mPort); + } +} + +PRUint32 +nsProtocolProxyService::SecondsSinceSessionStart() +{ + PRTime now = PR_Now(); + + // get time elapsed since session start + PRInt64 diff; + LL_SUB(diff, now, mSessionStart); + + // convert microseconds to seconds + PRTime ups; + LL_I2L(ups, PR_USEC_PER_SEC); + LL_DIV(diff, diff, ups); + + // convert to 32 bit value + PRUint32 dsec; + LL_L2UI(dsec, diff); + + return dsec; +} + +void +nsProtocolProxyService::EnableProxy(nsProxyInfo *pi) +{ + nsCAutoString key; + GetProxyKey(pi, key); + mFailedProxies.Remove(key); +} + +void +nsProtocolProxyService::DisableProxy(nsProxyInfo *pi) +{ + nsCAutoString key; + GetProxyKey(pi, key); + + PRUint32 dsec = SecondsSinceSessionStart(); + + // Add timeout to interval (this is the time when the proxy can + // be tried again). + dsec += mFailedProxyTimeout; + + // NOTE: The classic codebase would increase the timeout value + // incrementally each time a subsequent failure occured. + // We could do the same, but it would require that we not + // remove proxy entries in IsProxyDisabled or otherwise + // change the way we are recording disabled proxies. + // Simpler is probably better for now, and at least the + // user can tune the timeout setting via preferences. + + LOG(("DisableProxy %s %d\n", key.get(), dsec)); + + // If this fails, oh well... means we don't have enough memory + // to remember the failed proxy. + mFailedProxies.Put(key, dsec); +} + +PRBool +nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi) +{ + nsCAutoString key; + GetProxyKey(pi, key); + + PRUint32 val; + if (!mFailedProxies.Get(key, &val)) + return PR_FALSE; + + PRUint32 dsec = SecondsSinceSessionStart(); + + // if time passed has exceeded interval, then try proxy again. + if (dsec > val) { + mFailedProxies.Remove(key); + return PR_FALSE; + } + + return PR_TRUE; +} + +nsProxyInfo * +nsProtocolProxyService::BuildProxyList(const char *proxies, + PRBool permitHttp, + PRBool pruneDisabledProxies) +{ + nsProxyInfo *pi = nsnull, *first = nsnull, *last = nsnull; + while (*proxies) { + proxies = ExtractProxyInfo(proxies, permitHttp, &pi); + if (pi) { + if (pruneDisabledProxies && IsProxyDisabled(pi)) + NS_RELEASE(pi); + else { + if (last) { + NS_ASSERTION(last->mNext == nsnull, "leaking nsProxyInfo"); + last->mNext = pi; + } + else + first = pi; + + // since we are about to use this proxy, make sure it is not + // on the disabled proxy list. we'll add it back to that list + // if we have to (in GetFailoverForProxy). + EnableProxy(pi); + + last = pi; + } + } + } + return first; +} + +nsresult +nsProtocolProxyService::ExaminePACForProxy(nsIURI *aURI, + PRUint32 aProtoFlags, + nsIProxyInfo **aResult) +{ + // the following two failure cases can happen if we are queried before the + // PAC file is loaded. in these cases, we have no choice but to try a + // direct connection and hope for the best. the user will just have to + // press reload if the page doesn't load :-( + + if (!mPAC) { + NS_WARNING("PAC JS component is null; assuming DIRECT..."); + return NS_OK; + } + + nsCAutoString proxyStr; + nsresult rv = mPAC->GetProxyForURI(aURI, proxyStr); + if (NS_FAILED(rv)) { + NS_WARNING("PAC GetProxyForURI failed; assuming DIRECT..."); + return NS_OK; + } + if (proxyStr.IsEmpty()) { + NS_WARNING("PAC GetProxyForURI returned an empty string; assuming DIRECT..."); + return NS_OK; + } + + PRBool permitHttp = (aProtoFlags & nsIProtocolHandler::ALLOWS_PROXY_HTTP); + + // result is AddRef'd + nsProxyInfo *pi = BuildProxyList(proxyStr.get(), permitHttp, PR_TRUE); + if (pi) { + // + // if only DIRECT was specified then return no proxy info, and we're done. + // + if (!pi->mNext && pi->mType == kProxyType_DIRECT) + NS_RELEASE(pi); + } + else { + // + // if all of the proxy servers were marked invalid, then we'll go ahead + // and return the full list. this code works by rebuilding the list + // without pruning disabled proxies. + // + // we could have built the whole list and then removed those that were + // disabled, but it is less code to build the list twice. + // + pi = BuildProxyList(proxyStr.get(), permitHttp, PR_FALSE); + } + + *aResult = pi; + return NS_OK; +} + // nsIProtocolProxyService NS_IMETHODIMP nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxyInfo **aResult) @@ -490,68 +743,42 @@ nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxyInfo **aResult) // supposed to use a proxy, check for a proxy. if (0 == mUseProxy || (1 == mUseProxy && !CanUseProxy(aURI, defaultPort))) return NS_OK; + + // Proxy auto config magic... + if (2 == mUseProxy) + return ExaminePACForProxy(aURI, flags, aResult); // proxy info values const char *type = nsnull; - char *host = nsnull; + const nsACString *host = nsnull; PRInt32 port = -1; - - // Proxy auto config magic... - if (2 == mUseProxy) { - if (!mPAC) { - NS_ERROR("ERROR: PAC js component is null, assuming DIRECT"); - return NS_OK; // assume DIRECT connection for now - } - - nsCAutoString proxyStr; - rv = mPAC->GetProxyForURI(aURI, proxyStr); - if (NS_SUCCEEDED(rv)) { - PRBool permitHttp = (flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP); - nsProxyInfo *pi, *last = nsnull; - const char *p = proxyStr.get(); - while (*p) { - p = ExtractProxyInfo(p, permitHttp, &pi); - if (pi) { - if (last) - last->mNext = pi; - else - NS_ADDREF(*aResult = pi); - last = pi; - } - } - // if only DIRECT was specified then return no proxy info. - if (last && *aResult == last && last->mType == kProxyType_DIRECT) - NS_RELEASE(*aResult); - } - return NS_OK; - } if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 && scheme.EqualsLiteral("http")) { - host = ToNewCString(mHTTPProxyHost); + host = &mHTTPProxyHost; type = kProxyType_HTTP; port = mHTTPProxyPort; } else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 && scheme.EqualsLiteral("https")) { - host = ToNewCString(mHTTPSProxyHost); + host = &mHTTPSProxyHost; type = kProxyType_HTTP; port = mHTTPSProxyPort; } else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 && scheme.EqualsLiteral("ftp")) { - host = ToNewCString(mFTPProxyHost); + host = &mFTPProxyHost; type = kProxyType_HTTP; port = mFTPProxyPort; } else if (!mGopherProxyHost.IsEmpty() && mGopherProxyPort > 0 && scheme.EqualsLiteral("gopher")) { - host = ToNewCString(mGopherProxyHost); + host = &mGopherProxyHost; type = kProxyType_HTTP; port = mGopherProxyPort; } else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) { - host = ToNewCString(mSOCKSProxyHost); + host = &mSOCKSProxyHost; if (mSOCKSProxyVersion == 4) type = kProxyType_SOCKS4; else @@ -560,14 +787,14 @@ nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxyInfo **aResult) } if (type) - return NewProxyInfo_Internal(type, host, port, aResult); + return NewProxyInfo_Internal(type, *host, port, aResult); return NS_OK; } NS_IMETHODIMP -nsProtocolProxyService::NewProxyInfo(const char *aType, - const char *aHost, +nsProtocolProxyService::NewProxyInfo(const nsACString &aType, + const nsACString &aHost, PRInt32 aPort, nsIProxyInfo **aResult) { @@ -581,7 +808,7 @@ nsProtocolProxyService::NewProxyInfo(const char *aType, // proxy info instance. we just reference the string literals directly :) const char *type = nsnull; for (PRUint32 i=0; i eqs = - do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID); - if (!eqs) { - NS_ERROR("Failed to get EventQueue service"); - return rv; - } + // Now we need to setup a callback from the current thread, in which we + // will load the PAC file from the specified URI. Loading it now, causes + // re-entrancy problems for the XPCOM Service Manager (since we require the + // IO Service in order to load the PAC URI). - // get ui thread's event queue + // get current thread's event queue nsCOMPtr eq = nsnull; - rv = eqs->GetThreadEventQueue(NS_UI_THREAD, getter_AddRefs(eq)); - if (NS_FAILED(rv) || !eqs) { - NS_ERROR("Failed to get UI EventQueue"); + rv = NS_GetCurrentEventQ(getter_AddRefs(eq)); + if (NS_FAILED(rv) || !eq) { + NS_ERROR("Failed to get current event queue"); return rv; } @@ -643,7 +864,46 @@ NS_IMETHODIMP nsProtocolProxyService::GetProxyEnabled(PRBool *enabled) { NS_ENSURE_ARG_POINTER(enabled); - *enabled = mUseProxy; + *enabled = (mUseProxy != 0); + return NS_OK; +} + +NS_IMETHODIMP +nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo *aProxy, + nsIURI *aURI, + nsresult aStatus, + nsIProxyInfo **aResult) +{ + // We only support failover when a PAC file is configured. + if (mUseProxy != 2) + return NS_ERROR_NOT_AVAILABLE; + + // Verify that |aProxy| is one of our nsProxyInfo objects. + nsProxyInfo *pi = nsnull; + aProxy->QueryInterface(kProxyInfoID, (void **) &pi); + if (!pi) + return NS_ERROR_INVALID_ARG; + // OK, the QI checked out. We can proceed. Release the extra reference + // acquired by the call to QI now so we don't have to worry about it later. + // Moreover, call Release on |aProxy| instead of |pi| since we are going to + // use |pi| directly from now on. + aProxy->Release(); + + // Remember that this proxy is down. + DisableProxy(pi); + + // NOTE: At this point, we might want to prompt the user if we have + // not already tried going DIRECT. This is something that the + // classic codebase supported; however, IE6 does not prompt. + + if (!pi->mNext) + return NS_ERROR_NOT_AVAILABLE; + + LOG(("PAC failover from %s %s:%d to %s %s:%d\n", + pi->mType, pi->mHost.get(), pi->mPort, + pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort)); + + NS_ADDREF(*aResult = pi->mNext); return NS_OK; } @@ -779,7 +1039,7 @@ nsProtocolProxyService::LoadFilters(const char *filters) #endif mFiltersArray.AppendElement(hinfo); - hinfo = NULL; + hinfo = nsnull; loser: if (hinfo) delete hinfo; @@ -793,13 +1053,11 @@ nsProtocolProxyService::GetProtocolInfo(const char *aScheme, { nsresult rv; - if (!mIOService) { - mIOService = do_GetIOService(&rv); - if (NS_FAILED(rv)) return rv; - } + nsCOMPtr ios = do_GetIOService(&rv); + if (NS_FAILED(rv)) return rv; nsCOMPtr handler; - rv = mIOService->GetProtocolHandler(aScheme, getter_AddRefs(handler)); + rv = ios->GetProtocolHandler(aScheme, getter_AddRefs(handler)); if (NS_FAILED(rv)) return rv; rv = handler->GetProtocolFlags(&aFlags); @@ -810,12 +1068,11 @@ nsProtocolProxyService::GetProtocolInfo(const char *aScheme, nsresult nsProtocolProxyService::NewProxyInfo_Internal(const char *aType, - char *aHost, + const nsACString &aHost, PRInt32 aPort, nsIProxyInfo **aResult) { - nsProxyInfo *proxyInfo = nsnull; - NS_NEWXPCOM(proxyInfo, nsProxyInfo); + nsProxyInfo *proxyInfo = new nsProxyInfo(); if (!proxyInfo) return NS_ERROR_OUT_OF_MEMORY; diff --git a/mozilla/netwerk/base/src/nsProtocolProxyService.h b/mozilla/netwerk/base/src/nsProtocolProxyService.h index ca777ad149e..ac43e57fa1c 100644 --- a/mozilla/netwerk/base/src/nsProtocolProxyService.h +++ b/mozilla/netwerk/base/src/nsProtocolProxyService.h @@ -41,16 +41,23 @@ #include "plevent.h" #include "nsString.h" #include "nsCOMPtr.h" +#include "nsAutoPtr.h" #include "nsVoidArray.h" #include "nsIPrefBranch.h" #include "nsIProtocolProxyService.h" #include "nsIProxyAutoConfig.h" #include "nsIProxyInfo.h" -#include "nsIIOService.h" #include "nsIObserver.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "prtime.h" #include "prmem.h" #include "prio.h" +typedef nsDataHashtable nsFailedProxyTable; + +class nsProxyInfo; + class nsProtocolProxyService : public nsIProtocolProxyService , public nsIObserver { @@ -59,56 +66,27 @@ public: NS_DECL_NSIPROTOCOLPROXYSERVICE NS_DECL_NSIOBSERVER - nsProtocolProxyService(); - virtual ~nsProtocolProxyService(); + nsProtocolProxyService() NS_HIDDEN; + ~nsProtocolProxyService() NS_HIDDEN; - nsresult Init(); + NS_HIDDEN_(nsresult) Init(); void PrefsChanged(nsIPrefBranch *, const char* pref); - class nsProxyInfo : public nsIProxyInfo - { - public: - NS_DECL_ISUPPORTS - - NS_IMETHOD_(const char*) Host() { - return mHost; - } - - NS_IMETHOD_(PRInt32) Port() { - return mPort; - } - - NS_IMETHOD_(const char*) Type() { - return mType; - } - - NS_IMETHOD GetNext(nsIProxyInfo **result) { - NS_IF_ADDREF(*result = mNext); - return NS_OK; - } - - virtual ~nsProxyInfo() { - if (mHost) nsMemory::Free(mHost); - } - - nsProxyInfo() : mType(nsnull), mHost(nsnull), mPort(-1) { - } - - const char *mType; - char *mHost; // owning reference - PRInt32 mPort; - nsCOMPtr mNext; - }; - protected: - const char *ExtractProxyInfo(const char *proxy, PRBool permitHttp, nsProxyInfo **); - - nsresult GetProtocolInfo(const char *scheme, PRUint32 &flags, PRInt32 &defaultPort); - nsresult NewProxyInfo_Internal(const char *type, char *host, PRInt32 port, nsIProxyInfo **); - void LoadFilters(const char *filters); - PRBool CanUseProxy(nsIURI *aURI, PRInt32 defaultPort); + NS_HIDDEN_(const char *) ExtractProxyInfo(const char *proxy, PRBool permitHttp, nsProxyInfo **); + NS_HIDDEN_(nsProxyInfo *)BuildProxyList(const char *proxyStr, PRBool permitHttp, PRBool pruneDisabledProxies); + NS_HIDDEN_(void) GetProxyKey(nsProxyInfo *, nsCString &); + NS_HIDDEN_(PRUint32) SecondsSinceSessionStart(); + NS_HIDDEN_(void) EnableProxy(nsProxyInfo *); + NS_HIDDEN_(void) DisableProxy(nsProxyInfo *); + NS_HIDDEN_(PRBool) IsProxyDisabled(nsProxyInfo *); + NS_HIDDEN_(nsresult) ExaminePACForProxy(nsIURI *aURI, PRUint32 protoFlags, nsIProxyInfo **aResult); + NS_HIDDEN_(nsresult) GetProtocolInfo(const char *scheme, PRUint32 &flags, PRInt32 &defaultPort); + NS_HIDDEN_(nsresult) NewProxyInfo_Internal(const char *type, const nsACString &host, PRInt32 port, nsIProxyInfo **); + NS_HIDDEN_(void) LoadFilters(const char *filters); + NS_HIDDEN_(PRBool) CanUseProxy(nsIURI *aURI, PRInt32 defaultPort); static PRBool PR_CALLBACK CleanupFilterArray(void *aElement, void *aData); static void* PR_CALLBACK HandlePACLoadEvent(PLEvent* aEvent); @@ -150,31 +128,31 @@ protected: } }; - nsVoidArray mFiltersArray; + nsVoidArray mFiltersArray; + PRUint16 mUseProxy; - nsCOMPtr mIOService; + nsCString mHTTPProxyHost; + PRInt32 mHTTPProxyPort; - PRUint16 mUseProxy; + nsCString mFTPProxyHost; + PRInt32 mFTPProxyPort; - nsCString mHTTPProxyHost; - PRInt32 mHTTPProxyPort; + nsCString mGopherProxyHost; + PRInt32 mGopherProxyPort; - nsCString mFTPProxyHost; - PRInt32 mFTPProxyPort; - - nsCString mGopherProxyHost; - PRInt32 mGopherProxyPort; - - nsCString mHTTPSProxyHost; - PRInt32 mHTTPSProxyPort; + nsCString mHTTPSProxyHost; + PRInt32 mHTTPSProxyPort; - nsCString mSOCKSProxyHost; - PRInt32 mSOCKSProxyPort; - PRInt32 mSOCKSProxyVersion; + nsCString mSOCKSProxyHost; + PRInt32 mSOCKSProxyPort; + PRInt32 mSOCKSProxyVersion; nsCOMPtr mPAC; - nsCString mPACURL; + nsCString mPACURI; + + PRTime mSessionStart; + nsFailedProxyTable mFailedProxies; + PRInt32 mFailedProxyTimeout; }; #endif // !nsProtocolProxyService_h__ - diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp index 760df2f7059..88af3470076 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -895,28 +895,36 @@ nsHttpChannel::ProxyFailover() { LOG(("nsHttpChannel::ProxyFailover [this=%x]\n", this)); - NS_ASSERTION(mConnectionInfo->ProxyInfo(), "no proxy info"); + nsresult rv; + + nsCOMPtr pps = + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; nsCOMPtr pi; - mConnectionInfo->ProxyInfo()->GetNext(getter_AddRefs(pi)); - // if null result, then bail... - if (!pi) - return NS_ERROR_FAILURE; + rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus, + getter_AddRefs(pi)); + if (NS_FAILED(rv)) + return rv; - nsresult rv; nsCOMPtr newChannel; rv = gHttpHandler->NewProxiedChannel(mURI, pi, getter_AddRefs(newChannel)); - if (NS_SUCCEEDED(rv)) { - SetupReplacementChannel(mURI, newChannel, PR_TRUE); + if (NS_FAILED(rv)) + return rv; - // open new channel - rv = newChannel->AsyncOpen(mListener, mListenerContext); - if (NS_SUCCEEDED(rv)) { - mStatus = NS_BINDING_REDIRECTED; - mListener = nsnull; - mListenerContext = nsnull; - } - } + rv = SetupReplacementChannel(mURI, newChannel, PR_TRUE); + if (NS_FAILED(rv)) + return rv; + + // open new channel + rv = newChannel->AsyncOpen(mListener, mListenerContext); + if (NS_FAILED(rv)) + return rv; + + mStatus = NS_BINDING_REDIRECTED; + mListener = nsnull; + mListenerContext = nsnull; return rv; } @@ -3557,8 +3565,10 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) } // on proxy errors, try to failover - if (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED || - mStatus == NS_ERROR_UNKNOWN_PROXY_HOST) { + if (mConnectionInfo->ProxyInfo() && + (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED || + mStatus == NS_ERROR_UNKNOWN_PROXY_HOST || + mStatus == NS_ERROR_NET_TIMEOUT)) { if (NS_SUCCEEDED(ProxyFailover())) return NS_OK; }