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
This commit is contained in:
darin%meer.net 2004-07-01 23:31:47 +00:00
parent 8c5919cfec
commit 7e1bdbfb80
8 changed files with 531 additions and 218 deletions

View File

@ -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

View File

@ -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 <darin@meer.net>
*
* 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);
};

View File

@ -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;
};

View File

@ -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);

View File

@ -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__

View File

@ -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<nsIPrefBranchInternal> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
nsCOMPtr<nsIPrefBranchInternal> 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<nsIIOService> pIOService(do_GetIOService(&rv));
if (!pIOService || NS_FAILED(rv)) {
NS_ERROR("Cannot get IO Service");
return NULL;
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
if (NS_FAILED(rv)) {
NS_ERROR("No IO service");
return nsnull;
}
nsCOMPtr<nsIURI> 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<NS_ARRAY_LENGTH(types); ++i) {
if (PL_strcasecmp(aType, types[i]) == 0) {
if (aType.LowerCaseEqualsASCII(types[i]) == 0) {
type = types[i];
break;
}
@ -591,33 +818,27 @@ nsProtocolProxyService::NewProxyInfo(const char *aType,
if (aPort <= 0)
aPort = -1;
return NewProxyInfo_Internal(type, nsCRT::strdup(aHost), aPort, aResult);
return NewProxyInfo_Internal(type, aHost, aPort, aResult);
}
NS_IMETHODIMP
nsProtocolProxyService::ConfigureFromPAC(const char *url)
nsProtocolProxyService::ConfigureFromPAC(const nsACString &aURI)
{
nsresult rv = NS_OK;
mPACURL.Assign(url);
mPACURI = aURI;
mFailedProxies.Clear();
/* now we need to setup a callback from the main ui thread
in which we will load the pac file from the specified
url. loading it now, in the current thread results in a
browser crash */
nsresult rv;
// get event queue service
nsCOMPtr<nsIEventQueueService> 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<nsIEventQueue> 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<nsIIOService> ios = do_GetIOService(&rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIProtocolHandler> 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;

View File

@ -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<nsCStringHashKey, PRUint32> 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<nsIProxyInfo> 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<nsIIOService> 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<nsIProxyAutoConfig> mPAC;
nsCString mPACURL;
nsCString mPACURI;
PRTime mSessionStart;
nsFailedProxyTable mFailedProxies;
PRInt32 mFailedProxyTimeout;
};
#endif // !nsProtocolProxyService_h__

View File

@ -895,28 +895,36 @@ nsHttpChannel::ProxyFailover()
{
LOG(("nsHttpChannel::ProxyFailover [this=%x]\n", this));
NS_ASSERTION(mConnectionInfo->ProxyInfo(), "no proxy info");
nsresult rv;
nsCOMPtr<nsIProtocolProxyService> pps =
do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIProxyInfo> 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<nsIChannel> 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;
}