diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 22084f8bf8b9..a833ae9c6323 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -366,8 +366,6 @@ @BINPATH@/components/nsHelperAppDlg.js @BINPATH@/components/nsDownloadManagerUI.manifest @BINPATH@/components/nsDownloadManagerUI.js -@BINPATH@/components/nsProxyAutoConfig.manifest -@BINPATH@/components/nsProxyAutoConfig.js @BINPATH@/components/NetworkGeolocationProvider.manifest @BINPATH@/components/NetworkGeolocationProvider.js @BINPATH@/components/GPSDGeolocationProvider.manifest diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index e3d1851d0150..53d44666f3af 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -361,8 +361,6 @@ @BINPATH@/components/nsHelperAppDlg.js @BINPATH@/components/nsDownloadManagerUI.manifest @BINPATH@/components/nsDownloadManagerUI.js -@BINPATH@/components/nsProxyAutoConfig.manifest -@BINPATH@/components/nsProxyAutoConfig.js @BINPATH@/components/NetworkGeolocationProvider.manifest @BINPATH@/components/NetworkGeolocationProvider.js @BINPATH@/components/GPSDGeolocationProvider.manifest diff --git a/browser/installer/removed-files.in b/browser/installer/removed-files.in index 7d9e85af7b0a..49c6f86aac50 100644 --- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -87,6 +87,8 @@ components/xpcom.xpt components/xpti.dat components/xptitemp.dat components/nsMicrosummaryService.js +components/nsProxyAutoConfig.manifest +components/nsProxyAutoConfig.js D3DCompiler_42.dll d3dx9_42.dll defaults/pref/all.js @@ -921,6 +923,7 @@ xpicleanup@BIN_SUFFIX@ components/nsPlacesExpiration.js components/nsPrivateBrowsingService.js components/nsPrompter.js + components/nsProxyAutoConfig.manifest components/nsProxyAutoConfig.js components/nsSafebrowsingApplication.js components/nsSearchService.js diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 24ae5fb1c77a..1bef46932c9d 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -31,7 +31,7 @@ #include "nsIURL.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" -#include "nsIProtocolProxyService.h" +#include "nsIProtocolProxyService2.h" #include "nsIStreamConverterService.h" #include "nsIFile.h" #if defined(XP_MACOSX) @@ -743,12 +743,17 @@ nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result) nsCOMPtr uriIn; nsCOMPtr proxyService; + nsCOMPtr proxyService2; nsCOMPtr ioService; proxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res); if (NS_FAILED(res) || !proxyService) return res; + proxyService2 = do_QueryInterface(proxyService, &res); + if (NS_FAILED(res) || !proxyService) + return res; + ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res); if (NS_FAILED(res) || !ioService) return res; @@ -760,7 +765,8 @@ nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result) nsCOMPtr pi; - res = proxyService->Resolve(uriIn, 0, getter_AddRefs(pi)); + // Remove this with bug 778201 + res = proxyService2->DeprecatedBlockingResolve(uriIn, 0, getter_AddRefs(pi)); if (NS_FAILED(res)) return res; diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index aef140e31f99..2344453c63be 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -275,8 +275,6 @@ @BINPATH@/components/nsHelperAppDlg.js @BINPATH@/components/nsDownloadManagerUI.manifest @BINPATH@/components/nsDownloadManagerUI.js -@BINPATH@/components/nsProxyAutoConfig.manifest -@BINPATH@/components/nsProxyAutoConfig.js @BINPATH@/components/NetworkGeolocationProvider.manifest @BINPATH@/components/NetworkGeolocationProvider.js @BINPATH@/components/GPSDGeolocationProvider.manifest diff --git a/mobile/xul/installer/package-manifest.in b/mobile/xul/installer/package-manifest.in index a9396e34c6bd..58be25c63235 100644 --- a/mobile/xul/installer/package-manifest.in +++ b/mobile/xul/installer/package-manifest.in @@ -342,8 +342,6 @@ @BINPATH@/components/nsHelperAppDlg.js @BINPATH@/components/nsDownloadManagerUI.manifest @BINPATH@/components/nsDownloadManagerUI.js -@BINPATH@/components/nsProxyAutoConfig.manifest -@BINPATH@/components/nsProxyAutoConfig.js @BINPATH@/components/NetworkGeolocationProvider.manifest @BINPATH@/components/NetworkGeolocationProvider.js @BINPATH@/components/GPSDGeolocationProvider.manifest diff --git a/netwerk/base/public/Makefile.in b/netwerk/base/public/Makefile.in index 93ce67c47599..a1894762f26f 100644 --- a/netwerk/base/public/Makefile.in +++ b/netwerk/base/public/Makefile.in @@ -66,7 +66,6 @@ XPIDLSRCS = \ nsIProtocolProxyFilter.idl \ nsIProtocolProxyCallback.idl \ nsIProxiedProtocolHandler.idl \ - nsIProxyAutoConfig.idl \ nsIProxyInfo.idl \ nsITransport.idl \ nsISocketTransport.idl \ diff --git a/netwerk/base/public/nsIProtocolProxyService.idl b/netwerk/base/public/nsIProtocolProxyService.idl index a2aac086ef15..975f158568ea 100644 --- a/netwerk/base/public/nsIProtocolProxyService.idl +++ b/netwerk/base/public/nsIProtocolProxyService.idl @@ -17,24 +17,10 @@ interface nsIURI; * nsIProtocolProxyService provides methods to access information about * various network proxies. */ -[scriptable, uuid(d7ec6237-162e-40f5-a2b4-46ccd5fa83c9)] +[scriptable, uuid(e77c642b-026f-41ce-9b23-f829a6e3f300)] interface nsIProtocolProxyService : nsISupports { - /** - * This flag may be passed to the resolve method to request that it fail - * instead of block the calling thread. Proxy Auto Config (PAC) may - * perform a synchronous DNS query, which may not return immediately. So, - * calling resolve without this flag may result in locking up the calling - * thread for a lengthy period of time. - * - * By passing this flag to resolve, one can failover to asyncResolve to - * avoid locking up the calling thread if a PAC query is required. - * - * When this flag is passed to resolve, resolve may throw the exception - * NS_BASE_STREAM_WOULD_BLOCK to indicate that it failed due to this flag - * being present. - */ - const unsigned long RESOLVE_NON_BLOCKING = 1 << 0; + /** Flag 1 << 0 is unused **/ /** * When the proxy configuration is manual this flag may be passed to the @@ -77,44 +63,9 @@ interface nsIProtocolProxyService : nsISupports const unsigned long RESOLVE_ALWAYS_TUNNEL = (1 << 4); /** - * This method returns a nsIProxyInfo instance that identifies a proxy to - * be used for loading the given URI. Otherwise, this method returns null - * indicating that a direct connection should be used. - * - * @param aURI - * The URI to test. - * @param aFlags - * A bit-wise combination of the RESOLVE_ flags defined above. Pass - * 0 to specify the default behavior. Any additional bits that do - * not correspond to a RESOLVE_ flag are reserved for future use. - * - * 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 - * resolve 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. - * - * NOTE: If PAC is configured, and the PAC file has not yet been loaded, - * then this method will return a nsIProxyInfo instance with a type of - * "unknown" to indicate to the consumer that asyncResolve should be used - * to wait for the PAC file to finish loading. Otherwise, the consumer - * may choose to treat the result as type "direct" if desired. - * - * @see nsIProxiedProtocolHandler::newProxiedChannel - */ - nsIProxyInfo resolve(in nsIURI aURI, in unsigned long aFlags); - - /** - * This method is an asychronous version of the resolve method. Unlike - * resolve, this method is guaranteed not to block the calling thread - * waiting for DNS queries to complete. This method is intended as a - * substitute for resolve when the result is not needed immediately. + * This method returns via callback a nsIProxyInfo instance that identifies + * a proxy to be used for loading the given URI. Otherwise, this method returns + * null indicating that a direct connection should be used. * * @param aURI * The URI to test. @@ -128,6 +79,20 @@ interface nsIProtocolProxyService : nsISupports * @return An object that can be used to cancel the asychronous operation. * If canceled, the cancelation status (aReason) will be forwarded * to the callback's onProxyAvailable method via the aStatus param. + * + * 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 + * resolve 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 */ nsICancelable asyncResolve(in nsIURI aURI, in unsigned long aFlags, in nsIProtocolProxyCallback aCallback); diff --git a/netwerk/base/public/nsIProtocolProxyService2.idl b/netwerk/base/public/nsIProtocolProxyService2.idl index 42f4698f6ffd..22b1a0a6a909 100644 --- a/netwerk/base/public/nsIProtocolProxyService2.idl +++ b/netwerk/base/public/nsIProtocolProxyService2.idl @@ -9,7 +9,7 @@ /** * An extension of nsIProtocolProxyService */ -[scriptable, uuid(dbd9565d-29b1-437a-bff5-2fc339e2c5df)] +[scriptable, uuid(bed3702d-9374-4804-a20f-32baed8e2954)] interface nsIProtocolProxyService2 : nsIProtocolProxyService { /** @@ -17,4 +17,13 @@ interface nsIProtocolProxyService2 : nsIProtocolProxyService * reloaded. The PAC file is loaded asynchronously. */ void reloadPAC(); + + /** + * This exists so Java(tm) can migrate to an asynchronous interface. + * Do not use this unless you are the plugin interface, and even then you + * ought to feel horribly guilty because you will create main thread jank. + * + * No documentation - it is deprecated! + **/ + nsIProxyInfo deprecatedBlockingResolve(in nsIURI aURI, in unsigned long aFlags); }; diff --git a/netwerk/base/public/nsIProxiedProtocolHandler.idl b/netwerk/base/public/nsIProxiedProtocolHandler.idl index 49b7a46f7631..7830c9884f36 100644 --- a/netwerk/base/public/nsIProxiedProtocolHandler.idl +++ b/netwerk/base/public/nsIProxiedProtocolHandler.idl @@ -9,12 +9,26 @@ interface nsIChannel; interface nsIURI; interface nsIProxyInfo; -[scriptable, uuid(0a24fed4-1dd2-11b2-a75c-9f8b9a8f9ba7)] +[scriptable, uuid(2b63fe69-b0fc-48f2-a2df-adb795a4ce5c)] interface nsIProxiedProtocolHandler : nsIProtocolHandler { /** Create a new channel with the given proxyInfo * + * @param uri the channel uri + * @param proxyInfo any proxy information that has already been determined, + * or null if channel should later determine the proxy on its own using + * proxyResolveFlags/proxyURI + * @param proxyResolveFlags used if the proxy is later determined + * from nsIProtocolProxyService::asyncResolve + * @param proxyURI used if the proxy is later determined from + * nsIProtocolProxyService::asyncResolve with this as the proxyURI name. + * Generally this is the same as uri (or null which has the same + * effect), except in the case of websockets which wants to bootstrap + * to an http:// channel but make its proxy determination based on + * a ws:// uri. */ - nsIChannel newProxiedChannel(in nsIURI uri, in nsIProxyInfo proxyInfo); + nsIChannel newProxiedChannel(in nsIURI uri, in nsIProxyInfo proxyInfo, + in unsigned long proxyResolveFlags, + in nsIURI proxyURI); }; diff --git a/netwerk/base/public/nsIProxyAutoConfig.idl b/netwerk/base/public/nsIProxyAutoConfig.idl deleted file mode 100644 index 29e697236d9c..000000000000 --- a/netwerk/base/public/nsIProxyAutoConfig.idl +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -/** - * The nsIProxyAutoConfig interface is used for setting arbitrary proxy - * configurations based on the specified URL. - * - * Note this interface wraps (at least in the implementation) the older - * hacks of proxy auto config. - * - * - Gagan Saksena 04/23/00 - */ - -[scriptable, uuid(a42619df-0a1c-46fb-8154-0e9b8f8f1ea8)] -interface nsIProxyAutoConfig : nsISupports -{ - /** - * This method initializes the object. This method may be called multiple - * times. If either parameter is an empty value, then the object is - * reset to its initial state. - * - * @param aPACURI - * URI used to fetch the PAC script. This is needed for properly - * constructing the JS sandbox used to evaluate the PAC script. - * @param aPACScript - * Javascript program text. - */ - void init(in ACString aPACURI, in AString aPACScript); - - /** - * Get the proxy string for the specified URI. The proxy string is - * given by the following: - * - * result = proxy-spec *( proxy-sep proxy-spec ) - * proxy-spec = direct-type | proxy-type LWS proxy-host [":" proxy-port] - * direct-type = "DIRECT" - * proxy-type = "PROXY" | "SOCKS" | "SOCKS4" | "SOCKS5" - * proxy-sep = ";" LWS - * proxy-host = hostname | ipv4-address-literal - * proxy-port = - * LWS = *( SP | HT ) - * SP = - * HT = - * - * NOTE: direct-type and proxy-type are case insensitive - * NOTE: SOCKS implies SOCKS4 - * - * Examples: - * "PROXY proxy1.foo.com:8080; PROXY proxy2.foo.com:8080; DIRECT" - * "SOCKS socksproxy" - * "DIRECT" - * - * XXX add support for IPv6 address literals. - * XXX quote whatever the official standard is for PAC. - * - * @param aTestURI - * The URI as an ASCII string to test. - * @param aTestHost - * The ASCII hostname to test. - * - * @return PAC result string as defined above. - */ - ACString getProxyForURI(in ACString aTestURI, in ACString aTestHost); -}; diff --git a/netwerk/base/public/nsISystemProxySettings.idl b/netwerk/base/public/nsISystemProxySettings.idl index 48176f5102ef..eb99d012a92a 100644 --- a/netwerk/base/public/nsISystemProxySettings.idl +++ b/netwerk/base/public/nsISystemProxySettings.idl @@ -13,17 +13,31 @@ * either return the proper proxy data from the autoconfig URL specified in the system proxy, * or generate proxy data based on the system's manual proxy settings. */ -[scriptable, uuid(a9f3ae38-b769-4e0b-9317-578388e326c9)] +[scriptable, uuid(971591cd-277e-409a-bbf6-0a79879cd307)] interface nsISystemProxySettings : nsISupports { + /** + * Whether or not it is appropriate to execute getProxyForURI off the main thread. + * If that method can block (e.g. for WPAD as windows does) then it must be + * not mainThreadOnly to avoid creating main thread jank. The main thread only option is + * provided for implementations that do not block but use other main thread only + * functions such as dbus. + */ + readonly attribute bool mainThreadOnly; + /** * If non-empty, use this PAC file. If empty, call getProxyForURI instead. */ readonly attribute AUTF8String PACURI; /** - * See nsIProxyAutoConfig::getProxyForURI; this function behaves exactly - * the same way. + * See ProxyAutoConfig::getProxyForURI; this function behaves similarly except + * a more relaxed return string is allowed that includes full urls instead of just + * host:port syntax. e.g. "PROXY http://www.foo.com:8080" instead of + * "PROXY www.foo.com:8080" */ - AUTF8String getProxyForURI(in nsIURI aURI); + AUTF8String getProxyForURI(in AUTF8String testSpec, + in AUTF8String testScheme, + in AUTF8String testHost, + in int32_t testPort); }; diff --git a/netwerk/base/public/nsNetUtil.h b/netwerk/base/public/nsNetUtil.h index bf665cb31a49..ed9663ca4127 100644 --- a/netwerk/base/public/nsNetUtil.h +++ b/netwerk/base/public/nsNetUtil.h @@ -851,40 +851,6 @@ NS_GetReferrerFromChannel(nsIChannel *channel, return rv; } -#ifdef MOZILLA_INTERNAL_API -inline nsresult -NS_ExamineForProxy(const char *scheme, - const char *host, - int32_t port, - nsIProxyInfo **proxyInfo) -{ - nsresult rv; - nsCOMPtr pps = - do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); - if (NS_SUCCEEDED(rv)) { - nsAutoCString spec(scheme); - spec.Append("://"); - spec.Append(host); - spec.Append(':'); - spec.AppendInt(port); - // XXXXX - Under no circumstances whatsoever should any code which - // wants a uri do this. I do this here because I do not, in fact, - // actually want a uri (the dummy uris created here may not be - // syntactically valid for the specific protocol), and all we need - // is something which has a valid scheme, hostname, and a string - // to pass to PAC if needed - bbaetz - nsCOMPtr uri = - do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv); - if (NS_SUCCEEDED(rv)) { - rv = uri->SetSpec(spec); - if (NS_SUCCEEDED(rv)) - rv = pps->Resolve(uri, 0, proxyInfo); - } - } - return rv; -} -#endif - inline nsresult NS_ParseContentType(const nsACString &rawContentType, nsCString &contentType, diff --git a/netwerk/base/src/Makefile.in b/netwerk/base/src/Makefile.in index a12d3d20d1e2..035510fc048f 100644 --- a/netwerk/base/src/Makefile.in +++ b/netwerk/base/src/Makefile.in @@ -66,8 +66,11 @@ CPPSRCS = \ RedirectChannelRegistrar.cpp \ nsPreloadedStream.cpp \ nsStreamListenerWrapper.cpp \ + ProxyAutoConfig.cpp \ $(NULL) +LOCAL_INCLUDES += -I$(topsrcdir)/dom/base + ifeq ($(MOZ_WIDGET_TOOLKIT),os2) CPPSRCS += nsURLHelperOS2.cpp else @@ -97,11 +100,6 @@ ifdef MOZ_ENABLE_QTNETWORK endif endif -EXTRA_COMPONENTS = \ - $(srcdir)/nsProxyAutoConfig.js \ - $(srcdir)/nsProxyAutoConfig.manifest \ - $(NULL) - EXTRA_JS_MODULES = \ NetUtil.jsm \ $(NULL) diff --git a/netwerk/base/src/ProxyAutoConfig.cpp b/netwerk/base/src/ProxyAutoConfig.cpp new file mode 100644 index 000000000000..7b2cd85d7fa8 --- /dev/null +++ b/netwerk/base/src/ProxyAutoConfig.cpp @@ -0,0 +1,623 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProxyAutoConfig.h" +#include "jsapi.h" +#include "nsICancelable.h" +#include "nsIDNSListener.h" +#include "nsIDNSRecord.h" +#include "nsIDNSService.h" +#include "nsNetUtil.h" +#include "nsThreadUtils.h" +#include "nsIConsoleService.h" +#include "nsJSUtils.h" + +namespace mozilla { +namespace net { + +// These are some global helper symbols the PAC format requires that we provide that +// are initialized as part of the global javascript context used for PAC evaluations. +// Additionally dnsResolve(host) and myIpAddress() are supplied in the same context +// but are implemented as c++ helpers. proxyAlert(msg) is similarly defined, but that +// is a gecko specific extension. + +static const char *sPacUtils = + "function dnsDomainIs(host, domain) {\n" + " return (host.length >= domain.length &&\n" + " host.substring(host.length - domain.length) == domain);\n" + "}\n" + "" + "function dnsDomainLevels(host) {\n" + " return host.split('.').length - 1;\n" + "}\n" + "" + "function convert_addr(ipchars) {\n" + " var bytes = ipchars.split('.');\n" + " var result = ((bytes[0] & 0xff) << 24) |\n" + " ((bytes[1] & 0xff) << 16) |\n" + " ((bytes[2] & 0xff) << 8) |\n" + " (bytes[3] & 0xff);\n" + " return result;\n" + "}\n" + "" + "function isInNet(ipaddr, pattern, maskstr) {\n" + " var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n" + " if (test == null) {\n" + " ipaddr = dnsResolve(ipaddr);\n" + " if (ipaddr == null)\n" + " return false;\n" + " } else if (test[1] > 255 || test[2] > 255 || \n" + " test[3] > 255 || test[4] > 255) {\n" + " return false; // not an IP address\n" + " }\n" + " var host = convert_addr(ipaddr);\n" + " var pat = convert_addr(pattern);\n" + " var mask = convert_addr(maskstr);\n" + " return ((host & mask) == (pat & mask));\n" + " \n" + "}\n" + "" + "function isPlainHostName(host) {\n" + " return (host.search('\\\\.') == -1);\n" + "}\n" + "" + "function isResolvable(host) {\n" + " var ip = dnsResolve(host);\n" + " return (ip != null);\n" + "}\n" + "" + "function localHostOrDomainIs(host, hostdom) {\n" + " return (host == hostdom) ||\n" + " (hostdom.lastIndexOf(host + '.', 0) == 0);\n" + "}\n" + "" + "function shExpMatch(url, pattern) {\n" + " pattern = pattern.replace(/\\./g, '\\\\.');\n" + " pattern = pattern.replace(/\\*/g, '.*');\n" + " pattern = pattern.replace(/\\?/g, '.');\n" + " var newRe = new RegExp('^'+pattern+'$');\n" + " return newRe.test(url);\n" + "}\n" + "" + "var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" + "var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n" + "" + "function weekdayRange() {\n" + " function getDay(weekday) {\n" + " if (weekday in wdays) {\n" + " return wdays[weekday];\n" + " }\n" + " return -1;\n" + " }\n" + " var date = new Date();\n" + " var argc = arguments.length;\n" + " var wday;\n" + " if (argc < 1)\n" + " return false;\n" + " if (arguments[argc - 1] == 'GMT') {\n" + " argc--;\n" + " wday = date.getUTCDay();\n" + " } else {\n" + " wday = date.getDay();\n" + " }\n" + " var wd1 = getDay(arguments[0]);\n" + " var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" + " return (wd1 == -1 || wd2 == -1) ? false\n" + " : (wd1 <= wday && wday <= wd2);\n" + "}\n" + "" + "function dateRange() {\n" + " function getMonth(name) {\n" + " if (name in months) {\n" + " return months[name];\n" + " }\n" + " return -1;\n" + " }\n" + " var date = new Date();\n" + " var argc = arguments.length;\n" + " if (argc < 1) {\n" + " return false;\n" + " }\n" + " var isGMT = (arguments[argc - 1] == 'GMT');\n" + "\n" + " if (isGMT) {\n" + " argc--;\n" + " }\n" + " // function will work even without explict handling of this case\n" + " if (argc == 1) {\n" + " var tmp = parseInt(arguments[0]);\n" + " if (isNaN(tmp)) {\n" + " return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n" + " getMonth(arguments[0]));\n" + " } else if (tmp < 32) {\n" + " return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" + " } else { \n" + " return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==\n" + " tmp);\n" + " }\n" + " }\n" + " var year = date.getFullYear();\n" + " var date1, date2;\n" + " date1 = new Date(year, 0, 1, 0, 0, 0);\n" + " date2 = new Date(year, 11, 31, 23, 59, 59);\n" + " var adjustMonth = false;\n" + " for (var i = 0; i < (argc >> 1); i++) {\n" + " var tmp = parseInt(arguments[i]);\n" + " if (isNaN(tmp)) {\n" + " var mon = getMonth(arguments[i]);\n" + " date1.setMonth(mon);\n" + " } else if (tmp < 32) {\n" + " adjustMonth = (argc <= 2);\n" + " date1.setDate(tmp);\n" + " } else {\n" + " date1.setFullYear(tmp);\n" + " }\n" + " }\n" + " for (var i = (argc >> 1); i < argc; i++) {\n" + " var tmp = parseInt(arguments[i]);\n" + " if (isNaN(tmp)) {\n" + " var mon = getMonth(arguments[i]);\n" + " date2.setMonth(mon);\n" + " } else if (tmp < 32) {\n" + " date2.setDate(tmp);\n" + " } else {\n" + " date2.setFullYear(tmp);\n" + " }\n" + " }\n" + " if (adjustMonth) {\n" + " date1.setMonth(date.getMonth());\n" + " date2.setMonth(date.getMonth());\n" + " }\n" + " if (isGMT) {\n" + " var tmp = date;\n" + " tmp.setFullYear(date.getUTCFullYear());\n" + " tmp.setMonth(date.getUTCMonth());\n" + " tmp.setDate(date.getUTCDate());\n" + " tmp.setHours(date.getUTCHours());\n" + " tmp.setMinutes(date.getUTCMinutes());\n" + " tmp.setSeconds(date.getUTCSeconds());\n" + " date = tmp;\n" + " }\n" + " return ((date1 <= date) && (date <= date2));\n" + "}\n" + "" + "function timeRange() {\n" + " var argc = arguments.length;\n" + " var date = new Date();\n" + " var isGMT= false;\n" + "" + " if (argc < 1) {\n" + " return false;\n" + " }\n" + " if (arguments[argc - 1] == 'GMT') {\n" + " isGMT = true;\n" + " argc--;\n" + " }\n" + "\n" + " var hour = isGMT ? date.getUTCHours() : date.getHours();\n" + " var date1, date2;\n" + " date1 = new Date();\n" + " date2 = new Date();\n" + "\n" + " if (argc == 1) {\n" + " return (hour == arguments[0]);\n" + " } else if (argc == 2) {\n" + " return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" + " } else {\n" + " switch (argc) {\n" + " case 6:\n" + " date1.setSeconds(arguments[2]);\n" + " date2.setSeconds(arguments[5]);\n" + " case 4:\n" + " var middle = argc >> 1;\n" + " date1.setHours(arguments[0]);\n" + " date1.setMinutes(arguments[1]);\n" + " date2.setHours(arguments[middle]);\n" + " date2.setMinutes(arguments[middle + 1]);\n" + " if (middle == 2) {\n" + " date2.setSeconds(59);\n" + " }\n" + " break;\n" + " default:\n" + " throw 'timeRange: bad number of arguments'\n" + " }\n" + " }\n" + "\n" + " if (isGMT) {\n" + " date.setFullYear(date.getUTCFullYear());\n" + " date.setMonth(date.getUTCMonth());\n" + " date.setDate(date.getUTCDate());\n" + " date.setHours(date.getUTCHours());\n" + " date.setMinutes(date.getUTCMinutes());\n" + " date.setSeconds(date.getUTCSeconds());\n" + " }\n" + " return ((date1 <= date) && (date <= date2));\n" + "}\n" + ""; + +// The PACResolver is used for dnsResolve() +class PACResolver MOZ_FINAL : public nsIDNSListener +{ +public: + NS_DECL_ISUPPORTS + + PACResolver() + : mStatus(NS_ERROR_FAILURE) + { + } + + NS_IMETHODIMP OnLookupComplete(nsICancelable *request, + nsIDNSRecord *record, + nsresult status) + { + mRequest = nullptr; + mStatus = status; + mResponse = record; + return NS_OK; + } + + nsresult mStatus; + nsCOMPtr mRequest; + nsCOMPtr mResponse; +}; +NS_IMPL_THREADSAFE_ISUPPORTS1(PACResolver, nsIDNSListener) + +static +void PACLogToConsole(nsString &aMessage) +{ + nsCOMPtr consoleService = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (!consoleService) + return; + + consoleService->LogStringMessage(aMessage.get()); +} + +// Javascript errors are logged to the main error console +static void +PACErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) +{ + nsString formattedMessage(NS_LITERAL_STRING("PAC Execution Error: ")); + formattedMessage += report->ucmessage; + formattedMessage += NS_LITERAL_STRING(" ["); + formattedMessage += report->uclinebuf; + formattedMessage += NS_LITERAL_STRING("]"); + PACLogToConsole(formattedMessage); +} + +static +JSBool PACResolve(const nsCString &aHostName, nsCString &aDottedDecimal) +{ + nsCOMPtr dns = do_GetService(NS_DNSSERVICE_CONTRACTID); + nsCOMPtr helper = new PACResolver(); + if (!dns || NS_FAILED(dns->AsyncResolve(aHostName, 0, helper, + NS_GetCurrentThread(), + getter_AddRefs(helper->mRequest)))) + return false; + + // Spin the event loop of the pac thread until lookup is complete. + // nsPACman is responsible for keeping a queue and only allowing + // one PAC execution at a time even when it is called re-entrantly. + while (helper->mRequest) + NS_ProcessNextEvent(NS_GetCurrentThread()); + + if (NS_FAILED(helper->mStatus) || + NS_FAILED(helper->mResponse->GetNextAddrAsString(aDottedDecimal))) + return false; + return true; +} + +// dnsResolve(host) javascript implementation +static +JSBool PACDnsResolve(JSContext *cx, unsigned int argc, jsval *vp) +{ + if (NS_IsMainThread()) { + NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?"); + return false; + } + + JSString *arg1 = nullptr; + if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &arg1)) + return false; + + nsDependentJSString hostName; + nsCString dottedDecimal; + + if (!hostName.init(cx, arg1)) + return false; + if (!PACResolve(NS_ConvertUTF16toUTF8(hostName), dottedDecimal)) + return false; + + JSString *dottedDecimalString = JS_NewStringCopyZ(cx, dottedDecimal.get()); + JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(dottedDecimalString)); + return true; +} + +// myIpAddress() javascript implementation +static +JSBool PACMyIpAddress(JSContext *cx, unsigned int argc, jsval *vp) +{ + if (NS_IsMainThread()) { + NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?"); + return false; + } + + nsCString hostName; + + nsCOMPtr dns = do_GetService(NS_DNSSERVICE_CONTRACTID); + if (!dns || NS_FAILED(dns->GetMyHostName(hostName))) { + hostName.AssignLiteral("127.0.0.1"); + } + + nsCString dottedDecimal; + if (!PACResolve(hostName, dottedDecimal)) { + dottedDecimal.AssignLiteral("127.0.0.1"); + } + + JSString *dottedDecimalString = JS_NewStringCopyZ(cx, dottedDecimal.get()); + JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(dottedDecimalString)); + return true; +} + +// proxyAlert(msg) javascript implementation +static +JSBool PACProxyAlert(JSContext *cx, unsigned int argc, jsval *vp) +{ + JSString *arg1 = nullptr; + if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &arg1)) + return false; + + nsDependentJSString message; + if (!message.init(cx, arg1)) + return false; + + nsString alertMessage; + alertMessage.SetCapacity(32 + message.Length()); + alertMessage += NS_LITERAL_STRING("PAC-alert: "); + alertMessage += message; + PACLogToConsole(alertMessage); + + JS_SET_RVAL(cx, vp, JSVAL_VOID); /* return undefined */ + return true; +} + +static JSFunctionSpec PACGlobalFunctions[] = { + JS_FS("dnsResolve", PACDnsResolve, 1, 0), + JS_FS("myIpAddress", PACMyIpAddress, 0, 0), + JS_FS("proxyAlert", PACProxyAlert, 1, 0), + JS_FS_END +}; + +// JSRuntimeWrapper is a c++ object that manages the runtime and context +// for the JS engine used on the PAC thread. It is initialized and destroyed +// on the PAC thread. +class JSRuntimeWrapper +{ + public: + static JSRuntimeWrapper *Create() + { + JSRuntimeWrapper *entry = new JSRuntimeWrapper(); + + if (NS_FAILED(entry->Init())) { + delete entry; + return nullptr; + } + + return entry; + } + + JSContext *Context() const + { + return mContext; + } + + JSObject *Global() const + { + return mGlobal; + } + + ~JSRuntimeWrapper() + { + MOZ_COUNT_DTOR(JSRuntimeWrapper); + if (mContext) { + JS_DestroyContext(mContext); + } + + if (mRuntime) { + JS_DestroyRuntime(mRuntime); + } + } + + void SetOK() + { + mOK = true; + } + + bool IsOK() + { + return mOK; + } + +private: + static const unsigned sRuntimeHeapSize = 2 << 20; + + JSRuntime *mRuntime; + JSContext *mContext; + JSObject *mGlobal; + bool mOK; + + static JSClass sGlobalClass; + + JSRuntimeWrapper() + : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr), mOK(false) + { + MOZ_COUNT_CTOR(JSRuntimeWrapper); + } + + nsresult Init() + { + mRuntime = JS_NewRuntime(sRuntimeHeapSize); + NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY); + + mContext = JS_NewContext(mRuntime, 0); + NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY); + + JSAutoRequest ar(mContext); + + mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr); + NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY); + + JS_SetGlobalObject(mContext, mGlobal); + JS_InitStandardClasses(mContext, mGlobal); + + JS_SetVersion(mContext, JSVERSION_LATEST); + JS_SetErrorReporter(mContext, PACErrorReporter); + + if (!JS_DefineFunctions(mContext, mGlobal, PACGlobalFunctions)) + return NS_ERROR_FAILURE; + + return NS_OK; + } +}; + +JSClass JSRuntimeWrapper::sGlobalClass = { + "PACResolutionThreadGlobal", + JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub +}; + +nsresult +ProxyAutoConfig::Init(const nsCString &aPACURI, + const nsCString &aPACScript) +{ + mPACURI = aPACURI; + mPACScript = sPacUtils; + mPACScript.Append(aPACScript); + + if (!mRunning) + return SetupJS(); + + mJSNeedsSetup = true; + return NS_OK; +} + +nsresult +ProxyAutoConfig::SetupJS() +{ + mJSNeedsSetup = false; + NS_ABORT_IF_FALSE(!mRunning, "JIT is running"); + + delete mJSRuntime; + mJSRuntime = nullptr; + + if (mPACScript.IsEmpty()) + return NS_ERROR_FAILURE; + + mJSRuntime = JSRuntimeWrapper::Create(); + if (!mJSRuntime) + return NS_ERROR_FAILURE; + + JSAutoRequest ar(mJSRuntime->Context()); + + JSScript *script = JS_CompileScript(mJSRuntime->Context(), + mJSRuntime->Global(), + mPACScript.get(), mPACScript.Length(), + mPACURI.get(), 1); + if (!JS_ExecuteScript(mJSRuntime->Context(), mJSRuntime->Global(), script, nullptr)) { + nsString alertMessage(NS_LITERAL_STRING("PAC file failed to install from ")); + alertMessage += NS_ConvertUTF8toUTF16(mPACURI); + PACLogToConsole(alertMessage); + return NS_ERROR_FAILURE; + } + + mJSRuntime->SetOK(); + nsString alertMessage(NS_LITERAL_STRING("PAC file installed from ")); + alertMessage += NS_ConvertUTF8toUTF16(mPACURI); + PACLogToConsole(alertMessage); + + // we don't need these now + mPACScript.Truncate(); + mPACURI.Truncate(); + + return NS_OK; +} + +nsresult +ProxyAutoConfig::GetProxyForURI(const nsCString &aTestURI, + const nsCString &aTestHost, + nsACString &result) +{ + if (mJSNeedsSetup) + SetupJS(); + + if (!mJSRuntime || !mJSRuntime->IsOK()) + return NS_ERROR_NOT_AVAILABLE; + + JSContext *cx = mJSRuntime->Context(); + JSAutoRequest ar(cx); + + // the mRunning flag keeps a new PAC file from being installed + // while the event loop is spinning on a DNS function. Don't early return. + mRunning = true; + + nsresult rv = NS_ERROR_FAILURE; + JS::RootedString uriString(cx, JS_NewStringCopyZ(cx, aTestURI.get())); + JS::RootedString hostString(cx, JS_NewStringCopyZ(cx, aTestHost.get())); + + if (uriString && hostString) { + JS::RootedValue uriValue(cx, STRING_TO_JSVAL(uriString)); + JS::RootedValue hostValue(cx, STRING_TO_JSVAL(hostString)); + + jsval argv[2] = { uriValue, hostValue }; + jsval rval; + JSBool ok = JS_CallFunctionName(cx, mJSRuntime->Global(), + "FindProxyForURL", 2, argv, &rval); + + if (ok && rval.isString()) { + nsDependentJSString pacString; + if (pacString.init(cx, rval.toString())) { + CopyUTF16toUTF8(pacString, result); + rv = NS_OK; + } + } + } + mRunning = false; + return rv; +} + +void +ProxyAutoConfig::GC() +{ + if (!mJSRuntime || !mJSRuntime->IsOK()) + return; + + JS_MaybeGC(mJSRuntime->Context()); +} + +ProxyAutoConfig::~ProxyAutoConfig() +{ + MOZ_COUNT_DTOR(ProxyAutoConfig); + NS_ASSERTION(!mJSRuntime, + "~ProxyAutoConfig leaking JS runtime that " + "should have been deleted on pac thread"); +} + +void +ProxyAutoConfig::Shutdown() +{ + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread for shutdown"); + + if (mRunning || mShutdown) + return; + + mShutdown = true; + delete mJSRuntime; + mJSRuntime = nullptr; +} + +} // namespace mozilla +} // namespace mozilla::net diff --git a/netwerk/base/src/ProxyAutoConfig.h b/netwerk/base/src/ProxyAutoConfig.h new file mode 100644 index 000000000000..4aaaaef2a77a --- /dev/null +++ b/netwerk/base/src/ProxyAutoConfig.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ProxyAutoConfig_h__ +#define ProxyAutoConfig_h__ + +#include "nsString.h" + +namespace mozilla { namespace net { + +class JSRuntimeWrapper; + +// The ProxyAutoConfig class is meant to be created and run on a +// non main thread. It synchronously resolves PAC files by blocking that +// thread and running nested event loops. GetProxyForURI is not re-entrant. + +class ProxyAutoConfig { +public: + ProxyAutoConfig() + : mJSRuntime(nullptr) + , mRunning(false) + , mJSNeedsSetup(false) + , mShutdown(false) + { + MOZ_COUNT_CTOR(ProxyAutoConfig); + } + ~ProxyAutoConfig(); + + nsresult Init(const nsCString &aPACURI, + const nsCString &aPACScript); + void Shutdown(); + void GC(); + + /** + * Get the proxy string for the specified URI. The proxy string is + * given by the following: + * + * result = proxy-spec *( proxy-sep proxy-spec ) + * proxy-spec = direct-type | proxy-type LWS proxy-host [":" proxy-port] + * direct-type = "DIRECT" + * proxy-type = "PROXY" | "SOCKS" | "SOCKS4" | "SOCKS5" + * proxy-sep = ";" LWS + * proxy-host = hostname | ipv4-address-literal + * proxy-port = + * LWS = *( SP | HT ) + * SP = + * HT = + * + * NOTE: direct-type and proxy-type are case insensitive + * NOTE: SOCKS implies SOCKS4 + * + * Examples: + * "PROXY proxy1.foo.com:8080; PROXY proxy2.foo.com:8080; DIRECT" + * "SOCKS socksproxy" + * "DIRECT" + * + * XXX add support for IPv6 address literals. + * XXX quote whatever the official standard is for PAC. + * + * @param aTestURI + * The URI as an ASCII string to test. + * @param aTestHost + * The ASCII hostname to test. + * + * @param result + * result string as defined above. + */ + nsresult GetProxyForURI(const nsCString &aTestURI, + const nsCString &aTestHost, + nsACString &result); + +private: + // used to compile the PAC file and setup the execution context + nsresult SetupJS(); + + JSRuntimeWrapper *mJSRuntime; + bool mRunning; + bool mJSNeedsSetup; + bool mShutdown; + nsCString mPACScript; + nsCString mPACURI; +}; + +}} // namespace mozilla::net + +#endif // ProxyAutoConfig_h__ diff --git a/netwerk/base/src/nsBaseChannel.cpp b/netwerk/base/src/nsBaseChannel.cpp index 4eb85c1d7644..164ab4d44b07 100644 --- a/netwerk/base/src/nsBaseChannel.cpp +++ b/netwerk/base/src/nsBaseChannel.cpp @@ -701,7 +701,9 @@ nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) SUSPEND_PUMP_FOR_SCOPE(); - return mListener->OnStartRequest(this, mListenerContext); + if (mListener) // null in case of redirect + return mListener->OnStartRequest(this, mListenerContext); + return NS_OK; } NS_IMETHODIMP @@ -716,7 +718,8 @@ nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, // Cause IsPending to return false. mPump = nullptr; - mListener->OnStopRequest(this, mListenerContext, mStatus); + if (mListener) // null in case of redirect + mListener->OnStopRequest(this, mListenerContext, mStatus); mListener = nullptr; mListenerContext = nullptr; diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp index 1c225d81d641..75d2b86b4b59 100644 --- a/netwerk/base/src/nsIOService.cpp +++ b/netwerk/base/src/nsIOService.cpp @@ -39,6 +39,9 @@ #include "nsIConsoleService.h" #include "nsIUploadChannel2.h" #include "nsXULAppAPI.h" +#include "nsIProxiedChannel.h" +#include "nsIProtocolProxyCallback.h" +#include "nsICancelable.h" #include "mozilla/FunctionTimer.h" @@ -576,31 +579,6 @@ nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result) return NewChannelFromURIWithProxyFlags(aURI, nullptr, 0, result); } -void -nsIOService::LookupProxyInfo(nsIURI *aURI, - nsIURI *aProxyURI, - uint32_t aProxyFlags, - nsCString *aScheme, - nsIProxyInfo **outPI) -{ - nsresult rv; - nsCOMPtr pi; - - if (!mProxyService) { - mProxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID); - if (!mProxyService) - NS_WARNING("failed to get protocol proxy service"); - } - if (mProxyService) { - rv = mProxyService->Resolve(aProxyURI ? aProxyURI : aURI, aProxyFlags, - getter_AddRefs(pi)); - if (NS_FAILED(rv)) - pi = nullptr; - } - pi.forget(outPI); -} - - NS_IMETHODIMP nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI, nsIURI *aProxyURI, @@ -625,26 +603,11 @@ nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI, if (NS_FAILED(rv)) return rv; - // Talk to the PPS if the protocol handler allows proxying. Otherwise, - // skip this step. This allows us to lazily load the PPS at startup. - if (protoFlags & nsIProtocolHandler::ALLOWS_PROXY) { - nsCOMPtr pi; - LookupProxyInfo(aURI, aProxyURI, aProxyFlags, &scheme, getter_AddRefs(pi)); - if (pi) { - nsAutoCString type; - if (NS_SUCCEEDED(pi->GetType(type)) && type.EqualsLiteral("http")) { - // we are going to proxy this channel using an http proxy - rv = GetProtocolHandler("http", getter_AddRefs(handler)); - if (NS_FAILED(rv)) - return rv; - } - nsCOMPtr pph = do_QueryInterface(handler); - if (pph) - return pph->NewProxiedChannel(aURI, pi, result); - } - } - - rv = handler->NewChannel(aURI, result); + nsCOMPtr pph = do_QueryInterface(handler); + if (pph) + rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI, result); + else + rv = handler->NewChannel(aURI, result); NS_ENSURE_SUCCESS(rv, rv); // Some extensions override the http protocol handler and provide their own @@ -1212,35 +1175,79 @@ nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader, } // nsISpeculativeConnect -NS_IMETHODIMP -nsIOService::SpeculativeConnect(nsIURI *aURI, - nsIInterfaceRequestor *aCallbacks, - nsIEventTarget *aTarget) +class IOServiceProxyCallback MOZ_FINAL : public nsIProtocolProxyCallback { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPROTOCOLPROXYCALLBACK + + IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks, + nsIEventTarget *aTarget, + nsIOService *aIOService) + : mCallbacks(aCallbacks) + , mTarget(aTarget) + , mIOService(aIOService) + { } + +private: + nsRefPtr mCallbacks; + nsRefPtr mTarget; + nsRefPtr mIOService; +}; + +NS_IMPL_ISUPPORTS1(IOServiceProxyCallback, nsIProtocolProxyCallback) + +NS_IMETHODIMP +IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIURI *aURI, + nsIProxyInfo *pi, nsresult status) +{ + // Checking proxy status for speculative connect + nsAutoCString type; + if (NS_SUCCEEDED(status) && pi && + NS_SUCCEEDED(pi->GetType(type)) && + !type.EqualsLiteral("direct")) { + // proxies dont do speculative connect + return NS_OK; + } + nsAutoCString scheme; nsresult rv = aURI->GetScheme(scheme); if (NS_FAILED(rv)) - return rv; - - // Check for proxy information. If there is a proxy configured then a - // speculative connect should not be performed because the potential - // reward is slim with tcp peers closely located to the browser. - nsCOMPtr pi; - LookupProxyInfo(aURI, nullptr, 0, &scheme, getter_AddRefs(pi)); - if (pi) return NS_OK; nsCOMPtr handler; - rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler)); + rv = mIOService->GetProtocolHandler(scheme.get(), + getter_AddRefs(handler)); if (NS_FAILED(rv)) - return rv; + return NS_OK; nsCOMPtr speculativeHandler = do_QueryInterface(handler); if (!speculativeHandler) return NS_OK; - return speculativeHandler->SpeculativeConnect(aURI, - aCallbacks, - aTarget); + speculativeHandler->SpeculativeConnect(aURI, + mCallbacks, + mTarget); + return NS_OK; +} + +NS_IMETHODIMP +nsIOService::SpeculativeConnect(nsIURI *aURI, + nsIInterfaceRequestor *aCallbacks, + nsIEventTarget *aTarget) +{ + // Check for proxy information. If there is a proxy configured then a + // speculative connect should not be performed because the potential + // reward is slim with tcp peers closely located to the browser. + nsresult rv; + nsCOMPtr pps = + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr cancelable; + nsRefPtr callback = + new IOServiceProxyCallback(aCallbacks, aTarget, this); + return pps->AsyncResolve(aURI, 0, callback, getter_AddRefs(cancelable)); } diff --git a/netwerk/base/src/nsPACMan.cpp b/netwerk/base/src/nsPACMan.cpp index 9191709a382a..594d8c0830de 100644 --- a/netwerk/base/src/nsPACMan.cpp +++ b/netwerk/base/src/nsPACMan.cpp @@ -6,21 +6,24 @@ #include "nsPACMan.h" #include "nsThreadUtils.h" -#include "nsIDNSService.h" -#include "nsIDNSListener.h" -#include "nsICancelable.h" #include "nsIAuthPrompt.h" #include "nsIPromptFactory.h" #include "nsIHttpChannel.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsNetUtil.h" -#include "nsAutoPtr.h" #include "nsCRT.h" #include "prmon.h" #include "nsIAsyncVerifyRedirectCallback.h" +#include "nsProxyRelease.h" //----------------------------------------------------------------------------- +using namespace mozilla; +using namespace mozilla::net; + +// The PAC thread does evaluations of both PAC files and +// nsISystemProxySettings because they can both block the calling thread and we +// don't want that on the main thread // Check to see if the underlying request was not an error page in the case of // a HTTP request. For other types of channels, just return true. @@ -41,100 +44,206 @@ HttpRequestSucceeded(nsIStreamLoader *loader) //----------------------------------------------------------------------------- -// These objects are stored in nsPACMan::mPendingQ +// The ExecuteCallback runnable is triggered by +// nsPACManCallback::OnQueryComplete on the Main thread when its completion is +// discovered on the pac thread -class PendingPACQuery MOZ_FINAL : public PRCList, - public nsIDNSListener +class ExecuteCallback MOZ_FINAL : public nsRunnable { public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDNSLISTENER - - PendingPACQuery(nsPACMan *pacMan, nsIURI *uri, nsPACManCallback *callback) - : mPACMan(pacMan) - , mURI(uri) - , mCallback(callback) + ExecuteCallback(nsPACManCallback *aCallback, + nsresult status) + : mCallback(aCallback) + , mStatus(status) { - PR_INIT_CLIST(this); } - nsresult Start(uint32_t flags); - void Complete(nsresult status, const nsCString &pacString); + void SetPACString(const nsCString &pacString) + { + mPACString = pacString; + } + + void SetPACURL(const nsCString &pacURL) + { + mPACURL = pacURL; + } + + NS_IMETHODIMP Run() + { + mCallback->OnQueryComplete(mStatus, mPACString, mPACURL); + mCallback = nullptr; + return NS_OK; + } private: - nsPACMan *mPACMan; // weak reference - nsCOMPtr mURI; nsRefPtr mCallback; - nsCOMPtr mDNSRequest; + nsresult mStatus; + nsCString mPACString; + nsCString mPACURL; }; -// This is threadsafe because we implement nsIDNSListener -NS_IMPL_THREADSAFE_ISUPPORTS1(PendingPACQuery, nsIDNSListener) +//----------------------------------------------------------------------------- -nsresult -PendingPACQuery::Start(uint32_t flags) +// The PAC thread must be deleted from the main thread, this class +// acts as a proxy to do that, as the PACMan is reference counted +// and might be destroyed on either thread + +class ShutdownThread MOZ_FINAL : public nsRunnable { - if (mDNSRequest) - return NS_OK; // already started - - nsresult rv; - nsCOMPtr dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("unable to get the DNS service"); - return rv; +public: + ShutdownThread(nsIThread *thread) + : mThread(thread) + { } - nsAutoCString host; - rv = mURI->GetAsciiHost(host); - if (NS_FAILED(rv)) - return rv; + NS_IMETHODIMP Run() + { + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); + mThread->Shutdown(); + return NS_OK; + } - rv = dns->AsyncResolve(host, flags, this, NS_GetCurrentThread(), - getter_AddRefs(mDNSRequest)); - if (NS_FAILED(rv)) - NS_WARNING("DNS AsyncResolve failed"); +private: + nsCOMPtr mThread; +}; - return rv; +//----------------------------------------------------------------------------- + +// PACLoadComplete allows the PAC thread to tell the main thread that +// the javascript PAC file has been installed (perhaps unsuccessfully) +// and that there is no reason to queue executions anymore + +class PACLoadComplete MOZ_FINAL : public nsRunnable +{ +public: + PACLoadComplete(nsPACMan *aPACMan) + : mPACMan(aPACMan) + { + } + + NS_IMETHODIMP Run() + { + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); + mPACMan->mLoader = nullptr; + mPACMan->PostProcessPendingQ(); + return NS_OK; + } + +private: + nsRefPtr mPACMan; +}; + +//----------------------------------------------------------------------------- + +// ExecutePACThreadAction is used to proxy actions from the main +// thread onto the PAC thread. There are 3 options: process the queue, +// cancel the queue, and setup the javascript context with a new PAC file + +class ExecutePACThreadAction MOZ_FINAL : public nsRunnable +{ +public: + // by default we just process the queue + ExecutePACThreadAction(nsPACMan *aPACMan) + : mPACMan(aPACMan) + , mCancel(false) + , mSetupPAC(false) + { } + + void CancelQueue (nsresult status) + { + mCancel = true; + mCancelStatus = status; + } + + void SetupPAC (const char *text, uint32_t datalen, nsCString &pacURI) + { + mSetupPAC = true; + mSetupPACData.Assign(text, datalen); + mSetupPACURI = pacURI; + } + + NS_IMETHODIMP Run() + { + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); + if (mCancel) { + mPACMan->CancelPendingQ(mCancelStatus); + mCancel = false; + return NS_OK; + } + + if (mSetupPAC) { + mSetupPAC = false; + + mPACMan->mPAC.Init(mSetupPACURI, + mSetupPACData); + + nsRefPtr runnable = new PACLoadComplete(mPACMan); + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); + return NS_OK; + } + + mPACMan->ProcessPendingQ(); + return NS_OK; + } + +private: + nsRefPtr mPACMan; + + bool mCancel; + nsresult mCancelStatus; + + bool mSetupPAC; + nsCString mSetupPACData; + nsCString mSetupPACURI; +}; + +//----------------------------------------------------------------------------- + +PendingPACQuery::PendingPACQuery(nsPACMan *pacMan, nsIURI *uri, + nsPACManCallback *callback, + bool mainThreadResponse) + : mPACMan(pacMan) + , mCallback(callback) + , mOnMainThreadOnly(mainThreadResponse) +{ + uri->GetAsciiSpec(mSpec); + uri->GetAsciiHost(mHost); + uri->GetScheme(mScheme); + uri->GetPort(&mPort); } -// This may be called before or after OnLookupComplete void PendingPACQuery::Complete(nsresult status, const nsCString &pacString) { if (!mCallback) return; + nsRefPtr runnable = new ExecuteCallback(mCallback, status); + runnable->SetPACString(pacString); + if (mOnMainThreadOnly) + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); + else + runnable->Run(); +} - mCallback->OnQueryComplete(status, pacString); - mCallback = nullptr; +void +PendingPACQuery::UseAlternatePACFile(const nsCString &pacURL) +{ + if (!mCallback) + return; - if (mDNSRequest) { - mDNSRequest->Cancel(NS_ERROR_ABORT); - mDNSRequest = nullptr; - } + nsRefPtr runnable = new ExecuteCallback(mCallback, NS_OK); + runnable->SetPACURL(pacURL); + if (mOnMainThreadOnly) + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); + else + runnable->Run(); } NS_IMETHODIMP -PendingPACQuery::OnLookupComplete(nsICancelable *request, - nsIDNSRecord *record, - nsresult status) +PendingPACQuery::Run() { - // NOTE: we don't care about the results of this DNS query. We issued - // this DNS query just to pre-populate our DNS cache. - - mDNSRequest = nullptr; // break reference cycle - - // If we've already completed this query then do nothing. - if (!mCallback) - return NS_OK; - - // We're no longer pending, so we can remove ourselves. - PR_REMOVE_LINK(this); - - nsAutoCString pacString; - status = mPACMan->GetProxyForURI(mURI, pacString); - Complete(status, pacString); - - NS_RELEASE_THIS(); + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); + mPACMan->PostQuery(this); return NS_OK; } @@ -143,87 +252,87 @@ PendingPACQuery::OnLookupComplete(nsICancelable *request, nsPACMan::nsPACMan() : mLoadPending(false) , mShutdown(false) - , mScheduledReload(LL_MAXINT) , mLoadFailureCount(0) + , mInProgress(false) { - PR_INIT_CLIST(&mPendingQ); + NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be created on main thread"); } nsPACMan::~nsPACMan() { + if (mPACThread) { + if (NS_IsMainThread()) { + mPACThread->Shutdown(); + } + else { + nsRefPtr runnable = new ShutdownThread(mPACThread); + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); + } + } + if (!NS_IsMainThread()) { + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + + if (mPACURI) { + nsIURI *forgettable; + mPACURI.forget(&forgettable); + NS_ProxyRelease(mainThread, forgettable, false); + } + } + NS_ASSERTION(mLoader == nullptr, "pac man not shutdown properly"); - NS_ASSERTION(mPAC == nullptr, "pac man not shutdown properly"); - NS_ASSERTION(PR_CLIST_IS_EMPTY(&mPendingQ), "pac man not shutdown properly"); + NS_ASSERTION(mPendingQ.isEmpty(), "pac man not shutdown properly"); } void nsPACMan::Shutdown() { + NS_ABORT_IF_FALSE(NS_IsMainThread(), "pacman must be shutdown on main thread"); CancelExistingLoad(); - ProcessPendingQ(NS_ERROR_ABORT); - - mPAC = nullptr; mShutdown = true; + PostCancelPendingQ(NS_ERROR_ABORT); } nsresult -nsPACMan::GetProxyForURI(nsIURI *uri, nsACString &result) +nsPACMan::AsyncGetProxyForURI(nsIURI *uri, nsPACManCallback *callback, + bool mainThreadResponse) { - NS_ENSURE_STATE(!mShutdown); - - if (IsPACURI(uri)) { - result.Truncate(); - return NS_OK; - } - - MaybeReloadPAC(); - - if (IsLoading()) - return NS_ERROR_IN_PROGRESS; - if (!mPAC) + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); + if (mShutdown) return NS_ERROR_NOT_AVAILABLE; - nsAutoCString spec, host; - uri->GetAsciiSpec(spec); - uri->GetAsciiHost(host); + // Maybe Reload PAC + if (mPACURI && !mScheduledReload.IsNull() && + TimeStamp::Now() > mScheduledReload) + LoadPACFromURI(nullptr); - return mPAC->GetProxyForURI(spec, host, result); + nsRefPtr query = + new PendingPACQuery(this, uri, callback, mainThreadResponse); + + if (IsPACURI(uri)) { + // deal with this directly instead of queueing it + query->Complete(NS_OK, EmptyCString()); + return NS_OK; + } + + return mPACThread->Dispatch(query, nsIEventTarget::DISPATCH_NORMAL); } nsresult -nsPACMan::AsyncGetProxyForURI(nsIURI *uri, nsPACManCallback *callback) +nsPACMan::PostQuery(PendingPACQuery *query) { - NS_ENSURE_STATE(!mShutdown); + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); - MaybeReloadPAC(); - - PendingPACQuery *query = new PendingPACQuery(this, uri, callback); - if (!query) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(query); - PR_APPEND_LINK(query, &mPendingQ); - - // If we're waiting for the PAC file to load, then delay starting the query. - // See OnStreamComplete. However, if this is the PAC URI then query right - // away since we know the result will be DIRECT. We could shortcut some code - // in this case by issuing the callback directly from here, but that would - // require extra code, so we just go through the usual async code path. - int isPACURI = IsPACURI(uri); - - if (IsLoading() && !isPACURI) + if (mShutdown) { + query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString()); return NS_OK; - - nsresult rv = query->Start(isPACURI ? 0 : nsIDNSService::RESOLVE_SPECULATE); - if (rv == NS_ERROR_DNS_LOOKUP_QUEUE_FULL && !isPACURI) { - query->OnLookupComplete(NULL, NULL, NS_OK); - rv = NS_OK; - } else if (NS_FAILED(rv)) { - NS_WARNING("failed to start PAC query"); - PR_REMOVE_LINK(query); - NS_RELEASE(query); } - return rv; + // add a reference to the query while it is in the pending list + nsRefPtr addref(query); + mPendingQ.insertBack(addref.forget().get()); + ProcessPendingQ(); + return NS_OK; } nsresult @@ -256,21 +365,24 @@ nsPACMan::LoadPACFromURI(nsIURI *pacURI) mLoader = loader; if (pacURI) { mPACURI = pacURI; + mPACURI->GetSpec(mPACURISpec); mLoadFailureCount = 0; // reset } - mScheduledReload = LL_MAXINT; - mPAC = nullptr; + + // reset to Null + mScheduledReload = TimeStamp(); return NS_OK; } void nsPACMan::StartLoading() { + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); mLoadPending = false; // CancelExistingLoad was called... if (!mLoader) { - ProcessPendingQ(NS_ERROR_ABORT); + PostCancelPendingQ(NS_ERROR_ABORT); return; } @@ -293,18 +405,9 @@ nsPACMan::StartLoading() } CancelExistingLoad(); - ProcessPendingQ(NS_ERROR_UNEXPECTED); + PostCancelPendingQ(NS_ERROR_UNEXPECTED); } -void -nsPACMan::MaybeReloadPAC() -{ - if (!mPACURI) - return; - - if (PR_Now() > mScheduledReload) - LoadPACFromURI(nullptr); -} void nsPACMan::OnLoadFailure() @@ -324,11 +427,11 @@ nsPACMan::OnLoadFailure() if (!interval || interval > maxInterval) interval = maxInterval; -#ifdef DEBUG - printf("PAC load failure: will retry in %d seconds\n", interval); -#endif + mScheduledReload = TimeStamp::Now() + TimeDuration::FromSeconds(interval); - mScheduledReload = PR_Now() + int64_t(interval) * PR_USEC_PER_SEC; + // while we wait for the retry queued members should try direct + // even if that means fast failure. + PostCancelPendingQ(NS_ERROR_NOT_AVAILABLE); } void @@ -344,32 +447,110 @@ nsPACMan::CancelExistingLoad() } void -nsPACMan::ProcessPendingQ(nsresult status) +nsPACMan::PostProcessPendingQ() { - // Now, start any pending queries - PRCList *node = PR_LIST_HEAD(&mPendingQ); - while (node != &mPendingQ) { - PendingPACQuery *query = static_cast(node); - node = PR_NEXT_LINK(node); - if (NS_SUCCEEDED(status)) { - // keep the query in the list (so we can complete it from Shutdown if - // necessary). - status = query->Start(nsIDNSService::RESOLVE_SPECULATE); - } - if (status == NS_ERROR_DNS_LOOKUP_QUEUE_FULL) { - query->OnLookupComplete(NULL, NULL, NS_OK); - status = NS_OK; - } else if (NS_FAILED(status)) { - // remove the query from the list - PR_REMOVE_LINK(query); - query->Complete(status, EmptyCString()); - NS_RELEASE(query); - } - } + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); + nsRefPtr pending = + new ExecutePACThreadAction(this); + if (mPACThread) + mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); } -NS_IMPL_ISUPPORTS3(nsPACMan, nsIStreamLoaderObserver, nsIInterfaceRequestor, - nsIChannelEventSink) +void +nsPACMan::PostCancelPendingQ(nsresult status) +{ + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); + nsRefPtr pending = + new ExecutePACThreadAction(this); + pending->CancelQueue(status); + if (mPACThread) + mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); +} + +void +nsPACMan::CancelPendingQ(nsresult status) +{ + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); + nsRefPtr query; + + while (!mPendingQ.isEmpty()) { + query = dont_AddRef(mPendingQ.popLast()); + query->Complete(status, EmptyCString()); + } + + if (mShutdown) + mPAC.Shutdown(); +} + +void +nsPACMan::ProcessPendingQ() +{ + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); + while (ProcessPending()); + + // do GC while the thread has nothing pending + mPAC.GC(); + + if (mShutdown) + mPAC.Shutdown(); +} + +// returns true if progress was made by shortening the queue +bool +nsPACMan::ProcessPending() +{ + if (mPendingQ.isEmpty()) + return false; + + // queue during normal load, but if we are retrying a failed load then + // fast fail the queries + if (mInProgress || (IsLoading() && !mLoadFailureCount)) + return false; + + nsRefPtr query(dont_AddRef(mPendingQ.popFirst())); + + if (mShutdown || IsLoading()) { + query->Complete(NS_ERROR_NOT_AVAILABLE, EmptyCString()); + return true; + } + + nsAutoCString pacString; + bool completed = false; + mInProgress = true; + nsAutoCString PACURI; + + // first we need to consider the system proxy changing the pac url + if (mSystemProxySettings && + NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) && + !PACURI.IsEmpty() && + !PACURI.Equals(mPACURISpec)) { + query->UseAlternatePACFile(PACURI); + completed = true; + } + + // now try the system proxy settings for this particular url if + // PAC was not specified + if (!completed && mSystemProxySettings && PACURI.IsEmpty() && + NS_SUCCEEDED(mSystemProxySettings-> + GetProxyForURI(query->mSpec, query->mScheme, + query->mHost, query->mPort, + pacString))) { + query->Complete(NS_OK, pacString); + completed = true; + } + + // the systemproxysettings didn't complete the resolution. try via PAC + if (!completed) { + nsresult status = mPAC.GetProxyForURI(query->mSpec, query->mHost, pacString); + query->Complete(status, pacString); + } + + mInProgress = false; + return true; +} + +NS_IMPL_THREADSAFE_ISUPPORTS3(nsPACMan, nsIStreamLoaderObserver, + nsIInterfaceRequestor, nsIChannelEventSink) NS_IMETHODIMP nsPACMan::OnStreamComplete(nsIStreamLoader *loader, @@ -378,6 +559,7 @@ nsPACMan::OnStreamComplete(nsIStreamLoader *loader, uint32_t dataLen, const uint8_t *data) { + NS_ABORT_IF_FALSE(NS_IsMainThread(), "wrong thread"); if (mLoader != loader) { // If this happens, then it means that LoadPACFromURI was called more // than once before the initial call completed. In this case, status @@ -387,8 +569,6 @@ nsPACMan::OnStreamComplete(nsIStreamLoader *loader, return NS_OK; } - mLoader = nullptr; - if (NS_SUCCEEDED(status) && HttpRequestSucceeded(loader)) { // Get the URI spec used to load this PAC script. nsAutoCString pacURI; @@ -404,18 +584,21 @@ nsPACMan::OnStreamComplete(nsIStreamLoader *loader, } } - if (!mPAC) { - mPAC = do_CreateInstance(NS_PROXYAUTOCONFIG_CONTRACTID, &status); - if (!mPAC) - NS_WARNING("failed to instantiate PAC component"); - } - if (NS_SUCCEEDED(status)) { - // We assume that the PAC text is ASCII (or ISO-Latin-1). We've had this - // assumption forever, and some real-world PAC scripts actually have some - // non-ASCII text in comment blocks (see bug 296163). - const char *text = (const char *) data; - status = mPAC->Init(pacURI, NS_ConvertASCIItoUTF16(text, dataLen)); - } + // We assume that the PAC text is ASCII (or ISO-Latin-1). We've had this + // assumption forever, and some real-world PAC scripts actually have some + // non-ASCII text in comment blocks (see bug 296163). + const char *text = (const char *) data; + + // we have succeeded in loading the pac file using a bunch of interfaces that + // are main thread only, unfortunately we have to initialize the instance of + // the PAC evaluator (NS_PROXYAUTOCONFIG_CONTRACTID) on the pac thread, because + // that is where it will be used. + + nsRefPtr pending = + new ExecutePACThreadAction(this); + pending->SetupPAC(text, dataLen, pacURI); + if (mPACThread) + mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); // Even if the PAC file could not be parsed, we did succeed in loading the // data for it. @@ -426,11 +609,11 @@ nsPACMan::OnStreamComplete(nsIStreamLoader *loader, OnLoadFailure(); } - // Reset mPAC if necessary - if (mPAC && NS_FAILED(status)) - mPAC = nullptr; + if (NS_SUCCEEDED(status)) + PostProcessPendingQ(); + else + PostCancelPendingQ(status); - ProcessPendingQ(status); return NS_OK; } @@ -466,3 +649,26 @@ nsPACMan::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, callback->OnRedirectVerifyCallback(NS_OK); return NS_OK; } + +void +nsPACMan::NamePACThread() +{ + NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread"); + PR_SetCurrentThreadName("Proxy Resolution"); +} + +nsresult +nsPACMan::Init(nsISystemProxySettings *systemProxySettings) +{ + mSystemProxySettings = systemProxySettings; + + nsresult rv = NS_NewThread(getter_AddRefs(mPACThread), nullptr); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr event = NS_NewRunnableMethod(this, &nsPACMan::NamePACThread); + // don't check return value as it is not a big deal for this to fail. + mPACThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); + + return NS_OK; +} diff --git a/netwerk/base/src/nsPACMan.h b/netwerk/base/src/nsPACMan.h index 04ba4d7917d9..704ec4017b86 100644 --- a/netwerk/base/src/nsPACMan.h +++ b/netwerk/base/src/nsPACMan.h @@ -10,12 +10,20 @@ #include "nsIStreamLoader.h" #include "nsIInterfaceRequestor.h" #include "nsIChannelEventSink.h" -#include "nsIProxyAutoConfig.h" +#include "ProxyAutoConfig.h" +#include "nsICancelable.h" +#include "nsThreadUtils.h" #include "nsIURI.h" #include "nsCOMPtr.h" #include "nsString.h" -#include "prclist.h" #include "mozilla/Attributes.h" +#include "mozilla/LinkedList.h" +#include "nsIThread.h" +#include "nsAutoPtr.h" +#include "nsISystemProxySettings.h" +#include "mozilla/TimeStamp.h" + +class nsPACMan; /** * This class defines a callback interface used by AsyncGetProxyForURI. @@ -31,14 +39,45 @@ public: * @param pacString * This parameter holds the value of the PAC string. It is empty when * status is a failure code. + * @param newPACURL + * This parameter holds the URL of a new PAC file that should be loaded + * before the query is evaluated again. At least one of pacString and + * newPACURL should be 0 length. */ - virtual void OnQueryComplete(nsresult status, const nsCString &pacString) = 0; + virtual void OnQueryComplete(nsresult status, + const nsCString &pacString, + const nsCString &newPACURL) = 0; +}; + +class PendingPACQuery MOZ_FINAL : public nsRunnable, + public mozilla::LinkedListElement +{ +public: + PendingPACQuery(nsPACMan *pacMan, nsIURI *uri, + nsPACManCallback *callback, bool mainThreadResponse); + + // can be called from either thread + void Complete(nsresult status, const nsCString &pacString); + void UseAlternatePACFile(const nsCString &pacURL); + + nsCString mSpec; + nsCString mScheme; + nsCString mHost; + int32_t mPort; + + NS_IMETHOD Run(void); /* nsRunnable */ + +private: + nsPACMan *mPACMan; // weak reference + nsRefPtr mCallback; + bool mOnMainThreadOnly; }; /** * This class provides an abstraction layer above the PAC thread. The methods * defined on this class are intended to be called on the main thread only. */ + class nsPACMan MOZ_FINAL : public nsIStreamLoaderObserver , public nsIInterfaceRequestor , public nsIChannelEventSink @@ -55,19 +94,6 @@ public: */ void Shutdown(); - /** - * This method queries a PAC result synchronously. - * - * @param uri - * The URI to query. - * @param result - * Holds the PAC result string upon return. - * - * @return NS_ERROR_IN_PROGRESS if the PAC file is not yet loaded. - * @return NS_ERROR_NOT_AVAILABLE if the PAC file could not be loaded. - */ - nsresult GetProxyForURI(nsIURI *uri, nsACString &result); - /** * This method queries a PAC result asynchronously. The callback runs on the * calling thread. If the PAC file has not yet been loaded, then this method @@ -78,8 +104,11 @@ public: * The URI to query. * @param callback * The callback to run once the PAC result is available. + * @param mustCallbackOnMainThread + * If set to false the callback can be made from the PAC thread */ - nsresult AsyncGetProxyForURI(nsIURI *uri, nsPACManCallback *callback); + nsresult AsyncGetProxyForURI(nsIURI *uri, nsPACManCallback *callback, + bool mustCallbackOnMainThread); /** * This method may be called to reload the PAC file. While we are loading @@ -105,11 +134,28 @@ public: return mPACURI && NS_SUCCEEDED(mPACURI->Equals(uri, &result)) && result; } + bool IsPACURI(nsACString &spec) + { + nsAutoCString tmp; + return (mPACURI && NS_SUCCEEDED(mPACURI->GetSpec(tmp)) && tmp.Equals(spec)); + } + + NS_HIDDEN_(nsresult) Init(nsISystemProxySettings *); + static nsPACMan *sInstance; + + // PAC thread operations only + void ProcessPendingQ(); + void CancelPendingQ(nsresult); + private: NS_DECL_NSISTREAMLOADEROBSERVER NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSICHANNELEVENTSINK + friend class PendingPACQuery; + friend class PACLoadComplete; + friend class ExecutePACThreadAction; + ~nsPACMan(); /** @@ -117,13 +163,6 @@ private: */ void CancelExistingLoad(); - /** - * Process mPendingQ. If status is a failure code, then the pending queue - * will be emptied. If status is a success code, then the pending requests - * will be processed (i.e., their Start method will be called). - */ - void ProcessPendingQ(nsresult status); - /** * Start loading the PAC file. */ @@ -139,15 +178,35 @@ private: */ void OnLoadFailure(); + /** + * PostQuery() only runs on the PAC thread and it is used to + * place a pendingPACQuery into the queue and potentially + * execute the queue if it was otherwise empty + */ + nsresult PostQuery(PendingPACQuery *query); + + // PAC thread operations only + void PostProcessPendingQ(); + void PostCancelPendingQ(nsresult); + bool ProcessPending(); + void NamePACThread(); + private: - nsCOMPtr mPAC; + mozilla::net::ProxyAutoConfig mPAC; + nsCOMPtr mPACThread; + nsCOMPtr mSystemProxySettings; + + mozilla::LinkedList mPendingQ; /* pac thread only */ + nsCOMPtr mPACURI; - PRCList mPendingQ; + nsCString mPACURISpec; // for use off main thread nsCOMPtr mLoader; bool mLoadPending; bool mShutdown; - PRTime mScheduledReload; + mozilla::TimeStamp mScheduledReload; uint32_t mLoadFailureCount; + + bool mInProgress; }; #endif // nsPACMan_h__ diff --git a/netwerk/base/src/nsProtocolProxyService.cpp b/netwerk/base/src/nsProtocolProxyService.cpp index 52a11c8e4b50..91216cb7aa64 100644 --- a/netwerk/base/src/nsProtocolProxyService.cpp +++ b/netwerk/base/src/nsProtocolProxyService.cpp @@ -12,7 +12,6 @@ #include "nsIClassInfoImpl.h" #include "nsIServiceManager.h" #include "nsXPIDLString.h" -#include "nsIProxyAutoConfig.h" #include "nsIIOService.h" #include "nsIObserverService.h" #include "nsIProtocolHandler.h" @@ -29,6 +28,9 @@ #include "nsCRT.h" #include "prnetdb.h" #include "nsPACMan.h" +#include "nsProxyRelease.h" +#include "mozilla/Mutex.h" +#include "mozilla/CondVar.h" //---------------------------------------------------------------------------- @@ -72,12 +74,49 @@ public: , mDispatched(false) , mResolveFlags(aResolveFlags) , mPPS(pps) + , mXPComPPS(pps) , mURI(uri) , mCallback(callback) { NS_ASSERTION(mCallback, "null callback"); } + ~nsAsyncResolveRequest() + { + if (!NS_IsMainThread()) { + // these xpcom pointers might need to be proxied back to the + // main thread to delete safely, but if this request had its + // callbacks called normally they will all be null and this is a nop + + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + + if (mURI) { + nsIURI *forgettable; + mURI.forget(&forgettable); + NS_ProxyRelease(mainThread, forgettable, false); + } + + if (mCallback) { + nsIProtocolProxyCallback *forgettable; + mCallback.forget(&forgettable); + NS_ProxyRelease(mainThread, forgettable, false); + } + + if (mProxyInfo) { + nsIProxyInfo *forgettable; + mProxyInfo.forget(&forgettable); + NS_ProxyRelease(mainThread, forgettable, false); + } + + if (mXPComPPS) { + nsIProtocolProxyService *forgettable; + mXPComPPS.forget(&forgettable); + NS_ProxyRelease(mainThread, forgettable, false); + } + } + } + void SetResult(nsresult status, nsIProxyInfo *pi) { mStatus = status; @@ -124,7 +163,9 @@ private: // Called asynchronously, so we do not need to post another PLEvent // before calling DoCallback. - void OnQueryComplete(nsresult status, const nsCString &pacString) + void OnQueryComplete(nsresult status, + const nsCString &pacString, + const nsCString &newPACURL) { // If we've already called DoCallback then, nothing more to do. if (!mCallback) @@ -134,6 +175,7 @@ private: if (mStatus == NS_OK) { mStatus = status; mPACString = pacString; + mPACURL = newPACURL; } // In the cancelation case, we may still have another PLEvent in @@ -156,26 +198,59 @@ private: mPPS->ApplyFilters(mURI, info, mProxyInfo); else mProxyInfo = nullptr; + + LOG(("pac thread callback %s\n", mPACString.get())); + mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus); + } + else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) { + LOG(("pac thread callback indicates new pac file load\n")); + + // trigger load of new pac url + nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false); + if (NS_SUCCEEDED(rv)) { + // now that the load is triggered, we can resubmit the query + nsRefPtr newRequest = + new nsAsyncResolveRequest(mPPS, mURI, mResolveFlags, mCallback); + rv = mPPS->mPACMan->AsyncGetProxyForURI(mURI, newRequest, false); + } + + if (NS_FAILED(rv)) + mCallback->OnProxyAvailable(this, mURI, nullptr, rv); + + // do not call onproxyavailable() in SUCCESS case - the newRequest will + // take care of that + } + else { + LOG(("pac thread callback did not provide information %X\n", mStatus)); + mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus); } - mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus); + // We are on the main thread now and don't need these any more so + // release them to avoid having to proxy them back to the main thread + // in the dtor mCallback = nullptr; // in case the callback holds an owning ref to us + mPPS = nullptr; + mXPComPPS = nullptr; + mURI = nullptr; + mProxyInfo = nullptr; } private: nsresult mStatus; nsCString mPACString; + nsCString mPACURL; bool mDispatched; uint32_t mResolveFlags; - nsRefPtr mPPS; + nsProtocolProxyService *mPPS; + nsCOMPtr mXPComPPS; nsCOMPtr mURI; nsCOMPtr mCallback; nsCOMPtr mProxyInfo; }; -NS_IMPL_ISUPPORTS2(nsAsyncResolveRequest, nsICancelable, nsIRunnable) +NS_IMPL_THREADSAFE_ISUPPORTS2(nsAsyncResolveRequest, nsICancelable, nsIRunnable) //---------------------------------------------------------------------------- @@ -386,8 +461,12 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch, mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID); if (!mSystemProxySettings) mProxyConfig = PROXYCONFIG_DIRECT; + ResetPACThread(); } else { - mSystemProxySettings = nullptr; + if (mSystemProxySettings) { + mSystemProxySettings = nullptr; + ResetPACThread(); + } } } @@ -572,7 +651,7 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, *result = nullptr; uint32_t flags = 0; - // see BNF in nsIProxyAutoConfig.idl + // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl // find end of proxy info delimiter const char *end = start; @@ -616,33 +695,57 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, start = sp; while ((*start == ' ' || *start == '\t') && start < end) start++; - if (start < end) { - host = start; - hostEnd = strchr(host, ':'); - if (!hostEnd || hostEnd > end) { - hostEnd = end; - // no port, so assume default - if (type == kProxyType_HTTP) - port = 80; - else - port = 1080; + + // port defaults + if (type == kProxyType_HTTP) + port = 80; + else + port = 1080; + + nsProxyInfo *pi = new nsProxyInfo(); + pi->mType = type; + pi->mFlags = flags; + pi->mResolveFlags = aResolveFlags; + pi->mTimeout = mFailedProxyTimeout; + + // www.foo.com:8080 and http://www.foo.com:8080 + nsDependentCSubstring maybeURL(start, end - start); + nsCOMPtr pacURI; + + nsAutoCString urlHost; + if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) && + NS_SUCCEEDED(pacURI->GetAsciiHost(urlHost)) && + !urlHost.IsEmpty()) { + // http://www.example.com:8080 + + pi->mHost = urlHost; + + int32_t tPort; + if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) { + port = tPort; } - else - port = atoi(hostEnd + 1); + pi->mPort = port; } - nsProxyInfo *pi = new nsProxyInfo; - if (pi) { - pi->mType = type; - pi->mFlags = flags; - pi->mResolveFlags = aResolveFlags; - pi->mTimeout = mFailedProxyTimeout; + else { + // www.example.com:8080 + if (start < end) { + host = start; + hostEnd = strchr(host, ':'); + if (!hostEnd || hostEnd > end) { + hostEnd = end; + // no port, so assume default + } + else { + port = atoi(hostEnd + 1); + } + } // YES, it is ok to specify a null proxy host. if (host) { pi->mHost.Assign(host, hostEnd - host); pi->mPort = port; } - NS_ADDREF(*result = pi); } + NS_ADDREF(*result = pi); } while (*end == ';' || *end == ' ' || *end == '\t') @@ -739,15 +842,46 @@ nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi) return true; } +nsresult +nsProtocolProxyService::SetupPACThread() +{ + if (mPACMan) + return NS_OK; + + mPACMan = new nsPACMan(); + + bool mainThreadOnly; + nsresult rv; + if (mSystemProxySettings && + NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) && + !mainThreadOnly) { + rv = mPACMan->Init(mSystemProxySettings); + } + else { + rv = mPACMan->Init(nullptr); + } + + if (NS_FAILED(rv)) + mPACMan = nullptr; + return rv; +} + +nsresult +nsProtocolProxyService::ResetPACThread() +{ + if (!mPACMan) + return NS_OK; + + mPACMan->Shutdown(); + mPACMan = nullptr; + return SetupPACThread(); +} + nsresult nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec, bool forceReload) { - if (!mPACMan) { - mPACMan = new nsPACMan(); - if (!mPACMan) - return NS_ERROR_OUT_OF_MEMORY; - } + SetupPACThread(); nsCOMPtr pacURI; nsresult rv = NS_NewURI(getter_AddRefs(pacURI), spec); @@ -814,53 +948,125 @@ nsProtocolProxyService::ReloadPAC() return NS_OK; } -// nsIProtocolProxyService -NS_IMETHODIMP -nsProtocolProxyService::Resolve(nsIURI *uri, uint32_t flags, - nsIProxyInfo **result) +// When sync interface is removed this can go away too +class nsAsyncBridgeRequest MOZ_FINAL : public nsPACManCallback { - NS_ENSURE_ARG_POINTER(uri); + NS_DECL_ISUPPORTS + + nsAsyncBridgeRequest() + : mMutex("nsDeprecatedCallback") + , mCondVar(mMutex, "nsDeprecatedCallback") + , mCompleted(false) + { + } + + void OnQueryComplete(nsresult status, + const nsCString &pacString, + const nsCString &newPACURL) + { + MutexAutoLock lock(mMutex); + mCompleted = true; + mStatus = status; + mPACString = pacString; + mPACURL = newPACURL; + mCondVar.Notify(); + } + + void Lock() { mMutex.Lock(); } + void Unlock() { mMutex.Unlock(); } + void Wait() { mCondVar.Wait(PR_SecondsToInterval(3)); } + +private: + ~nsAsyncBridgeRequest() + { + } + + friend class nsProtocolProxyService; + + Mutex mMutex; + CondVar mCondVar; + + nsresult mStatus; + nsCString mPACString; + nsCString mPACURL; + bool mCompleted; +}; +NS_IMPL_THREADSAFE_ISUPPORTS1(nsAsyncBridgeRequest, nsPACManCallback) + +// nsIProtocolProxyService2 +NS_IMETHODIMP +nsProtocolProxyService::DeprecatedBlockingResolve(nsIURI *aURI, + uint32_t aFlags, + nsIProxyInfo **retval) +{ + NS_ENSURE_ARG_POINTER(aURI); nsProtocolInfo info; - nsresult rv = GetProtocolInfo(uri, &info); + nsresult rv = GetProtocolInfo(aURI, &info); if (NS_FAILED(rv)) return rv; - bool usePAC; - rv = Resolve_Internal(uri, info, flags, &usePAC, result); - if (NS_FAILED(rv)) { - LOG(("Resolve_Internal returned rv(0x%08x)\n", rv)); + nsCOMPtr pi; + bool usePACThread; + + // SystemProxySettings and PAC files can block the main thread + // but if neither of them are in use, we can just do the work + // right here and directly invoke the callback + + rv = Resolve_Internal(aURI, info, aFlags, &usePACThread, getter_AddRefs(pi)); + if (NS_FAILED(rv)) return rv; + + if (!usePACThread || !mPACMan) { + ApplyFilters(aURI, info, pi); + pi.forget(retval); + return NS_OK; } - if (usePAC && mPACMan) { - NS_ASSERTION(*result == nullptr, "we should not have a result yet"); + // Use the PAC thread to do the work, so we don't have to reimplement that + // code, but block this thread on that completion. + nsRefPtr ctx = new nsAsyncBridgeRequest(); + ctx->Lock(); + if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForURI(aURI, ctx, false))) { + // this can really block the main thread, so cap it at 3 seconds + ctx->Wait(); + } + ctx->Unlock(); + if (!ctx->mCompleted) + return NS_ERROR_FAILURE; + if (NS_FAILED(ctx->mStatus)) + return ctx->mStatus; - // If the caller didn't want us to invoke PAC, then error out. - if (flags & RESOLVE_NON_BLOCKING) - return NS_BASE_STREAM_WOULD_BLOCK; + // pretty much duplicate real DoCallback logic - // Query the PAC file synchronously. - nsCString pacString; - rv = mPACMan->GetProxyForURI(uri, pacString); - if (NS_SUCCEEDED(rv)) - ProcessPACString(pacString, flags, result); - else if (rv == NS_ERROR_IN_PROGRESS) { - // Construct a special UNKNOWN proxy entry that informs the caller - // that the proxy info is yet to be determined. - rv = NewProxyInfo_Internal(kProxyType_UNKNOWN, EmptyCString(), -1, - 0, 0, nullptr, flags, result); - if (NS_FAILED(rv)) - return rv; - } - else - NS_WARNING("failed querying PAC file; trying DIRECT"); + // Generate proxy info from the PAC string if appropriate + if (!ctx->mPACString.IsEmpty()) { + LOG(("sync pac thread callback %s\n", ctx->mPACString.get())); + ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi)); + ApplyFilters(aURI, info, pi); + pi.forget(retval); + return NS_OK; } - ApplyFilters(uri, info, result); + if (!ctx->mPACURL.IsEmpty()) { + NS_WARNING("sync pac thread callback indicates new pac file load\n"); + // This is a problem and is one of the reasons this blocking interface + // is deprecated. The main loop needs to spin to make this reload happen. So + // we are going to kick off the reload and return an error - it will work + // next time. Because this sync interface is only used in the java plugin it + // is extremely likely that the pac file has already been loaded anyhow. + + rv = ConfigureFromPAC(ctx->mPACURL, false); + if (NS_FAILED(rv)) + return rv; + return NS_ERROR_NOT_AVAILABLE; + } + + *retval = nullptr; return NS_OK; } +// nsIProtocolProxyService NS_IMETHODIMP nsProtocolProxyService::AsyncResolve(nsIURI *uri, uint32_t flags, nsIProtocolProxyCallback *callback, @@ -871,29 +1077,33 @@ nsProtocolProxyService::AsyncResolve(nsIURI *uri, uint32_t flags, nsRefPtr ctx = new nsAsyncResolveRequest(this, uri, flags, callback); - if (!ctx) - return NS_ERROR_OUT_OF_MEMORY; nsProtocolInfo info; nsresult rv = GetProtocolInfo(uri, &info); if (NS_FAILED(rv)) return rv; - bool usePAC; nsCOMPtr pi; - rv = Resolve_Internal(uri, info, flags, &usePAC, getter_AddRefs(pi)); + bool usePACThread; + + // SystemProxySettings and PAC files can block the main thread + // but if neither of them are in use, we can just do the work + // right here and directly invoke the callback + + rv = Resolve_Internal(uri, info, flags, &usePACThread, getter_AddRefs(pi)); if (NS_FAILED(rv)) return rv; - if (!usePAC || !mPACMan) { + if (!usePACThread || !mPACMan) { + // we can do it locally ApplyFilters(uri, info, pi); - ctx->SetResult(NS_OK, pi); return ctx->DispatchCallback(); } - // else kick off a PAC query - rv = mPACMan->AsyncGetProxyForURI(uri, ctx); + // else kick off a PAC thread query + + rv = mPACMan->AsyncGetProxyForURI(uri, ctx, true); if (NS_SUCCEEDED(rv)) { *result = ctx; NS_ADDREF(*result); @@ -1241,42 +1451,76 @@ nsresult nsProtocolProxyService::Resolve_Internal(nsIURI *uri, const nsProtocolInfo &info, uint32_t flags, - bool *usePAC, + bool *usePACThread, nsIProxyInfo **result) { NS_ENSURE_ARG_POINTER(uri); + nsresult rv = SetupPACThread(); + if (NS_FAILED(rv)) + return rv; - *usePAC = false; + *usePACThread = false; *result = nullptr; if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY)) return NS_OK; // Can't proxy this (filters may not override) - if (mSystemProxySettings) { + // See bug #586908. + // Avoid endless loop if |uri| is the current PAC-URI. Returning OK + // here means that we will not use a proxy for this connection. + if (mPACMan && mPACMan->IsPACURI(uri)) + return NS_OK; + + bool mainThreadOnly; + if (mSystemProxySettings && + mProxyConfig == PROXYCONFIG_SYSTEM && + NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) && + !mainThreadOnly) { + *usePACThread = true; + return NS_OK; + } + + if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) { + // If the system proxy setting implementation is not threadsafe (e.g + // linux gconf), we'll do it inline here. Such implementations promise + // not to block + nsAutoCString PACURI; - if (NS_FAILED(mSystemProxySettings->GetPACURI(PACURI)) || - PACURI.IsEmpty()) { - nsAutoCString proxy; - nsresult rv = mSystemProxySettings->GetProxyForURI(uri, proxy); - if (NS_SUCCEEDED(rv)) { - ProcessPACString(proxy, flags, result); + nsAutoCString pacString; + + if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) && + !PACURI.IsEmpty()) { + // There is a PAC URI configured. If it is unchanged, then + // just execute the PAC thread. If it is changed then load + // the new value + + if (mPACMan && mPACMan->IsPACURI(PACURI)) { + // unchanged + *usePACThread = true; return NS_OK; } - // no proxy, stop search + + ConfigureFromPAC(PACURI, false); return NS_OK; } - // See bug #586908. - // Avoid endless loop if |uri| is the current PAC-URI. Returning OK - // here means that we will not use a proxy for this connection. - if (mPACMan && mPACMan->IsPACURI(uri)) - return NS_OK; + nsAutoCString spec; + nsAutoCString host; + nsAutoCString scheme; + int32_t port = -1; - // Switch to new PAC file if that setting has changed. If the setting - // hasn't changed, ConfigureFromPAC will exit early. - nsresult rv = ConfigureFromPAC(PACURI, false); - if (NS_FAILED(rv)) - return rv; + uri->GetAsciiSpec(spec); + uri->GetAsciiHost(host); + uri->GetScheme(scheme); + uri->GetPort(&port); + + // now try the system proxy settings for this particular url + if (NS_SUCCEEDED(mSystemProxySettings-> + GetProxyForURI(spec, scheme, host, port, + pacString))) { + ProcessPACString(pacString, 0, result); + return NS_OK; + } } // if proxies are enabled and this host:port combo is supposed to use a @@ -1287,10 +1531,9 @@ nsProtocolProxyService::Resolve_Internal(nsIURI *uri, return NS_OK; // Proxy auto config magic... - if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD || - mProxyConfig == PROXYCONFIG_SYSTEM) { + if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) { // Do not query PAC now. - *usePAC = true; + *usePACThread = true; return NS_OK; } @@ -1304,7 +1547,7 @@ nsProtocolProxyService::Resolve_Internal(nsIURI *uri, if ((flags & RESOLVE_PREFER_SOCKS_PROXY) && !mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) { host = &mSOCKSProxyHost; - if (mSOCKSProxyVersion == 4) + if (mSOCKSProxyVersion == 4) type = kProxyType_SOCKS4; else type = kProxyType_SOCKS; @@ -1341,7 +1584,7 @@ nsProtocolProxyService::Resolve_Internal(nsIURI *uri, } else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) { host = &mSOCKSProxyHost; - if (mSOCKSProxyVersion == 4) + if (mSOCKSProxyVersion == 4) type = kProxyType_SOCKS4; else type = kProxyType_SOCKS; @@ -1351,9 +1594,9 @@ nsProtocolProxyService::Resolve_Internal(nsIURI *uri, } if (type) { - nsresult rv = NewProxyInfo_Internal(type, *host, port, proxyFlags, - PR_UINT32_MAX, nullptr, flags, - result); + rv = NewProxyInfo_Internal(type, *host, port, proxyFlags, + PR_UINT32_MAX, nullptr, flags, + result); if (NS_FAILED(rv)) return rv; } @@ -1411,7 +1654,7 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info, // Start by removing all disallowed proxies if required: if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) { - nsProxyInfo *last = nullptr, *iter = head; + nsProxyInfo *last = nullptr, *iter = head; while (iter) { if (iter->Type() == kProxyType_HTTP) { // reject! @@ -1435,7 +1678,7 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info, // Now, scan to see if all remaining proxies are disabled. If so, then // we'll just bail and return them all. Otherwise, we'll go and prune the // disabled ones. - + bool allDisabled = true; nsProxyInfo *iter; @@ -1450,7 +1693,7 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info, LOG(("All proxies are disabled, so trying all again")); else { // remove any disabled proxies. - nsProxyInfo *last = nullptr; + nsProxyInfo *last = nullptr; for (iter = head; iter; ) { if (IsProxyDisabled(iter)) { // reject! diff --git a/netwerk/base/src/nsProtocolProxyService.h b/netwerk/base/src/nsProtocolProxyService.h index ebc4b6d90e17..875ed59de44f 100644 --- a/netwerk/base/src/nsProtocolProxyService.h +++ b/netwerk/base/src/nsProtocolProxyService.h @@ -13,7 +13,6 @@ #include "nsIPrefBranch.h" #include "nsIProtocolProxyService2.h" #include "nsIProtocolProxyFilter.h" -#include "nsIProxyAutoConfig.h" #include "nsISystemProxySettings.h" #include "nsIProxyInfo.h" #include "nsIObserver.h" @@ -272,6 +271,10 @@ protected: */ NS_HIDDEN_(bool) CanUseProxy(nsIURI *uri, int32_t defaultPort); +private: + nsresult SetupPACThread(); + nsresult ResetPACThread(); + public: // The Sun Forte compiler and others implement older versions of the // C++ standard's rules on access and nested classes. These structs diff --git a/netwerk/base/src/nsProxyAutoConfig.js b/netwerk/base/src/nsProxyAutoConfig.js deleted file mode 100644 index 204d24187d2b..000000000000 --- a/netwerk/base/src/nsProxyAutoConfig.js +++ /dev/null @@ -1,314 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; c-basic-offset: 4; -*- */ -/* vim:set ts=4 sw=4 sts=4 et: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - Script for Proxy Auto Config in the new world order. - - Gagan Saksena 04/24/00 -*/ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); - -const kDNS_CONTRACTID = "@mozilla.org/network/dns-service;1"; - -const nsISupports = Components.interfaces.nsISupports; -const nsIProxyAutoConfig = Components.interfaces.nsIProxyAutoConfig; -const nsIDNSService = Components.interfaces.nsIDNSService; - -var dns; - -// implementor of nsIProxyAutoConfig -function nsProxyAutoConfig() { - dns = Components.classes[kDNS_CONTRACTID].getService(nsIDNSService); -}; - -nsProxyAutoConfig.prototype = { - classID: Components.ID("63ac8c66-1dd2-11b2-b070-84d00d3eaece"), - - // sandbox in which we eval loaded autoconfig js file - _sandBox: null, - - QueryInterface: XPCOMUtils.generateQI([nsIProxyAutoConfig]), - - init: function(pacURI, pacText) { - // remove PAC configuration if requested - if (pacURI == "" || pacText == "") { - this._sandBox = null; - return; - } - - // allocate a fresh Sandbox to clear global scope for new PAC script - this._sandBox = new Components.utils.Sandbox(pacURI, - {sandboxName: 'nsProxyAutoConfig'}); - Components.utils.evalInSandbox(pacUtils, this._sandBox); - - // add predefined functions to pac - this._sandBox.importFunction(myIpAddress); - this._sandBox.importFunction(dnsResolve); - this._sandBox.importFunction(proxyAlert, "alert"); - - // evaluate loaded js file - Components.utils.evalInSandbox(pacText, this._sandBox); - }, - - getProxyForURI: function(testURI, testHost) { - if (!("FindProxyForURL" in this._sandBox)) - return null; - - // Call the original function - try { - var rval = this._sandBox.FindProxyForURL(testURI, testHost); - } catch (e) { - throw XPCSafeJSObjectWrapper(e); - } - return rval; - } -} - -function proxyAlert(msg) { - try { - // It would appear that the console service is threadsafe. - var cns = Components.classes["@mozilla.org/consoleservice;1"] - .getService(Components.interfaces.nsIConsoleService); - cns.logStringMessage("PAC-alert: "+msg); - } catch (e) { - dump("PAC: proxyAlert ERROR: "+e+"\n"); - } -} - -// wrapper for getting local IP address called by PAC file -function myIpAddress() { - try { - return dns.resolve(dns.myHostName, 0).getNextAddrAsString(); - } catch (e) { - return '127.0.0.1'; - } -} - -// wrapper for resolving hostnames called by PAC file -function dnsResolve(host) { - try { - return dns.resolve(host, 0).getNextAddrAsString(); - } catch (e) { - return null; - } -} - -NSGetFactory = XPCOMUtils.generateNSGetFactory([nsProxyAutoConfig]); - -var pacUtils = -"function dnsDomainIs(host, domain) {\n" + -" return (host.length >= domain.length &&\n" + -" host.substring(host.length - domain.length) == domain);\n" + -"}\n" + - -"function dnsDomainLevels(host) {\n" + -" return host.split('.').length-1;\n" + -"}\n" + - -"function convert_addr(ipchars) {\n"+ -" var bytes = ipchars.split('.');\n"+ -" var result = ((bytes[0] & 0xff) << 24) |\n"+ -" ((bytes[1] & 0xff) << 16) |\n"+ -" ((bytes[2] & 0xff) << 8) |\n"+ -" (bytes[3] & 0xff);\n"+ -" return result;\n"+ -"}\n"+ - -"function isInNet(ipaddr, pattern, maskstr) {\n"+ -" var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n"+ -" if (test == null) {\n"+ -" ipaddr = dnsResolve(ipaddr);\n"+ -" if (ipaddr == null)\n"+ -" return false;\n"+ -" } else if (test[1] > 255 || test[2] > 255 || \n"+ -" test[3] > 255 || test[4] > 255) {\n"+ -" return false; // not an IP address\n"+ -" }\n"+ -" var host = convert_addr(ipaddr);\n"+ -" var pat = convert_addr(pattern);\n"+ -" var mask = convert_addr(maskstr);\n"+ -" return ((host & mask) == (pat & mask));\n"+ -" \n"+ -"}\n"+ - -"function isPlainHostName(host) {\n" + -" return (host.search('\\\\.') == -1);\n" + -"}\n" + - -"function isResolvable(host) {\n" + -" var ip = dnsResolve(host);\n" + -" return (ip != null);\n" + -"}\n" + - -"function localHostOrDomainIs(host, hostdom) {\n" + -" return (host == hostdom) ||\n" + -" (hostdom.lastIndexOf(host + '.', 0) == 0);\n" + -"}\n" + - -"function shExpMatch(url, pattern) {\n" + -" pattern = pattern.replace(/\\./g, '\\\\.');\n" + -" pattern = pattern.replace(/\\*/g, '.*');\n" + -" pattern = pattern.replace(/\\?/g, '.');\n" + -" var newRe = new RegExp('^'+pattern+'$');\n" + -" return newRe.test(url);\n" + -"}\n" + - -"var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" + - -"var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n"+ - -"function weekdayRange() {\n" + -" function getDay(weekday) {\n" + -" if (weekday in wdays) {\n" + -" return wdays[weekday];\n" + -" }\n" + -" return -1;\n" + -" }\n" + -" var date = new Date();\n" + -" var argc = arguments.length;\n" + -" var wday;\n" + -" if (argc < 1)\n" + -" return false;\n" + -" if (arguments[argc - 1] == 'GMT') {\n" + -" argc--;\n" + -" wday = date.getUTCDay();\n" + -" } else {\n" + -" wday = date.getDay();\n" + -" }\n" + -" var wd1 = getDay(arguments[0]);\n" + -" var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" + -" return (wd1 == -1 || wd2 == -1) ? false\n" + -" : (wd1 <= wday && wday <= wd2);\n" + -"}\n" + - -"function dateRange() {\n" + -" function getMonth(name) {\n" + -" if (name in months) {\n" + -" return months[name];\n" + -" }\n" + -" return -1;\n" + -" }\n" + -" var date = new Date();\n" + -" var argc = arguments.length;\n" + -" if (argc < 1) {\n" + -" return false;\n" + -" }\n" + -" var isGMT = (arguments[argc - 1] == 'GMT');\n" + -"\n" + -" if (isGMT) {\n" + -" argc--;\n" + -" }\n" + -" // function will work even without explict handling of this case\n" + -" if (argc == 1) {\n" + -" var tmp = parseInt(arguments[0]);\n" + -" if (isNaN(tmp)) {\n" + -" return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n" + -"getMonth(arguments[0]));\n" + -" } else if (tmp < 32) {\n" + -" return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" + -" } else { \n" + -" return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==\n" + -"tmp);\n" + -" }\n" + -" }\n" + -" var year = date.getFullYear();\n" + -" var date1, date2;\n" + -" date1 = new Date(year, 0, 1, 0, 0, 0);\n" + -" date2 = new Date(year, 11, 31, 23, 59, 59);\n" + -" var adjustMonth = false;\n" + -" for (var i = 0; i < (argc >> 1); i++) {\n" + -" var tmp = parseInt(arguments[i]);\n" + -" if (isNaN(tmp)) {\n" + -" var mon = getMonth(arguments[i]);\n" + -" date1.setMonth(mon);\n" + -" } else if (tmp < 32) {\n" + -" adjustMonth = (argc <= 2);\n" + -" date1.setDate(tmp);\n" + -" } else {\n" + -" date1.setFullYear(tmp);\n" + -" }\n" + -" }\n" + -" for (var i = (argc >> 1); i < argc; i++) {\n" + -" var tmp = parseInt(arguments[i]);\n" + -" if (isNaN(tmp)) {\n" + -" var mon = getMonth(arguments[i]);\n" + -" date2.setMonth(mon);\n" + -" } else if (tmp < 32) {\n" + -" date2.setDate(tmp);\n" + -" } else {\n" + -" date2.setFullYear(tmp);\n" + -" }\n" + -" }\n" + -" if (adjustMonth) {\n" + -" date1.setMonth(date.getMonth());\n" + -" date2.setMonth(date.getMonth());\n" + -" }\n" + -" if (isGMT) {\n" + -" var tmp = date;\n" + -" tmp.setFullYear(date.getUTCFullYear());\n" + -" tmp.setMonth(date.getUTCMonth());\n" + -" tmp.setDate(date.getUTCDate());\n" + -" tmp.setHours(date.getUTCHours());\n" + -" tmp.setMinutes(date.getUTCMinutes());\n" + -" tmp.setSeconds(date.getUTCSeconds());\n" + -" date = tmp;\n" + -" }\n" + -" return ((date1 <= date) && (date <= date2));\n" + -"}\n" + - -"function timeRange() {\n" + -" var argc = arguments.length;\n" + -" var date = new Date();\n" + -" var isGMT= false;\n"+ -"\n" + -" if (argc < 1) {\n" + -" return false;\n" + -" }\n" + -" if (arguments[argc - 1] == 'GMT') {\n" + -" isGMT = true;\n" + -" argc--;\n" + -" }\n" + -"\n" + -" var hour = isGMT ? date.getUTCHours() : date.getHours();\n" + -" var date1, date2;\n" + -" date1 = new Date();\n" + -" date2 = new Date();\n" + -"\n" + -" if (argc == 1) {\n" + -" return (hour == arguments[0]);\n" + -" } else if (argc == 2) {\n" + -" return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" + -" } else {\n" + -" switch (argc) {\n" + -" case 6:\n" + -" date1.setSeconds(arguments[2]);\n" + -" date2.setSeconds(arguments[5]);\n" + -" case 4:\n" + -" var middle = argc >> 1;\n" + -" date1.setHours(arguments[0]);\n" + -" date1.setMinutes(arguments[1]);\n" + -" date2.setHours(arguments[middle]);\n" + -" date2.setMinutes(arguments[middle + 1]);\n" + -" if (middle == 2) {\n" + -" date2.setSeconds(59);\n" + -" }\n" + -" break;\n" + -" default:\n" + -" throw 'timeRange: bad number of arguments'\n" + -" }\n" + -" }\n" + -"\n" + -" if (isGMT) {\n" + -" date.setFullYear(date.getUTCFullYear());\n" + -" date.setMonth(date.getUTCMonth());\n" + -" date.setDate(date.getUTCDate());\n" + -" date.setHours(date.getUTCHours());\n" + -" date.setMinutes(date.getUTCMinutes());\n" + -" date.setSeconds(date.getUTCSeconds());\n" + -" }\n" + -" return ((date1 <= date) && (date <= date2));\n" + -"}\n" - diff --git a/netwerk/base/src/nsProxyAutoConfig.manifest b/netwerk/base/src/nsProxyAutoConfig.manifest deleted file mode 100644 index ade07352fcef..000000000000 --- a/netwerk/base/src/nsProxyAutoConfig.manifest +++ /dev/null @@ -1,2 +0,0 @@ -component {63ac8c66-1dd2-11b2-b070-84d00d3eaece} nsProxyAutoConfig.js -contract @mozilla.org/network/proxy-auto-config;1 {63ac8c66-1dd2-11b2-b070-84d00d3eaece} diff --git a/netwerk/protocol/ftp/nsFTPChannel.h b/netwerk/protocol/ftp/nsFTPChannel.h index aa512eb97970..cde6d9d36721 100644 --- a/netwerk/protocol/ftp/nsFTPChannel.h +++ b/netwerk/protocol/ftp/nsFTPChannel.h @@ -55,6 +55,11 @@ public: return mProxyInfo; } + void SetProxyInfo(nsIProxyInfo *pi) + { + mProxyInfo = pi; + } + // Were we asked to resume a download? bool ResumeRequested() { return mResumeRequested; } diff --git a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp index 06a3c30f257b..8e02e70c2b17 100644 --- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp +++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp @@ -33,6 +33,8 @@ #include "nsIStringBundle.h" #include "nsAuthInformationHolder.h" #include "nsICharsetConverterManager.h" +#include "nsIProtocolProxyService.h" +#include "nsICancelable.h" #if defined(PR_LOGGING) extern PRLogModuleInfo* gFTPLog; @@ -50,12 +52,13 @@ removeParamsFromPath(nsCString& path) } } -NS_IMPL_ISUPPORTS_INHERITED4(nsFtpState, +NS_IMPL_ISUPPORTS_INHERITED5(nsFtpState, nsBaseContentStream, nsIInputStreamCallback, nsITransportEventSink, nsICacheListener, - nsIRequestObserver) + nsIRequestObserver, + nsIProtocolProxyCallback) nsFtpState::nsFtpState() : nsBaseContentStream(true) @@ -78,6 +81,7 @@ nsFtpState::nsFtpState() , mAddressChecked(false) , mServerIsIPv6(false) , mControlStatus(NS_OK) + , mDeferredCallbackPending(false) { LOG_ALWAYS(("FTP:(%x) nsFtpState created", this)); @@ -89,6 +93,9 @@ nsFtpState::~nsFtpState() { LOG_ALWAYS(("FTP:(%x) nsFtpState destroyed", this)); + if (mProxyRequest) + mProxyRequest->Cancel(NS_ERROR_FAILURE); + // release reference to handler nsFtpProtocolHandler *handler = gFtpHandler; NS_RELEASE(handler); @@ -1759,6 +1766,19 @@ nsFtpState::Init(nsFtpChannel *channel) if (port > 0) mPort = port; + // Lookup Proxy information asynchronously if it isn't already set + // on the channel and if we aren't configured explicitly to go directly + uint32_t proxyConfigType; + nsCOMPtr pps = + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID); + + if (pps && !mChannel->ProxyInfo() && + NS_SUCCEEDED(pps->GetProxyConfigType(&proxyConfigType)) && + proxyConfigType != nsIProtocolProxyService::PROXYCONFIG_DIRECT) { + pps->AsyncResolve(mChannel->URI(), 0, this, + getter_AddRefs(mProxyRequest)); + } + return NS_OK; } @@ -2155,6 +2175,67 @@ nsFtpState::CloseWithStatus(nsresult status) return nsBaseContentStream::CloseWithStatus(status); } +static nsresult +CreateHTTPProxiedChannel(nsIURI *uri, nsIProxyInfo *pi, nsIChannel **newChannel) +{ + nsresult rv; + nsCOMPtr ioService = do_GetIOService(&rv); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr handler; + rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr pph = do_QueryInterface(handler, &rv); + if (NS_FAILED(rv)) + return rv; + + return pph->NewProxiedChannel(uri, pi, 0, nullptr, newChannel); +} + +NS_IMETHODIMP +nsFtpState::OnProxyAvailable(nsICancelable *request, nsIURI *uri, + nsIProxyInfo *pi, nsresult status) +{ + mProxyRequest = nullptr; + + // failed status code just implies DIRECT processing + + if (NS_SUCCEEDED(status)) { + nsAutoCString type; + if (pi && NS_SUCCEEDED(pi->GetType(type)) && type.EqualsLiteral("http")) { + // Proxy the FTP url via HTTP + // This would have been easier to just return a HTTP channel directly + // from nsIIOService::NewChannelFromURI(), but the proxy type cannot + // be reliabliy determined synchronously without jank due to pac, etc.. + LOG(("FTP:(%p) Configured to use a HTTP proxy channel\n", this)); + + nsCOMPtr newChannel; + if (NS_SUCCEEDED(CreateHTTPProxiedChannel(uri, pi, + getter_AddRefs(newChannel))) && + NS_SUCCEEDED(mChannel->Redirect(newChannel, + nsIChannelEventSink::REDIRECT_INTERNAL, + true))) { + LOG(("FTP:(%p) Redirected to use a HTTP proxy channel\n", this)); + return NS_OK; + } + } + else if (pi) { + // Proxy using the FTP protocol routed through a socks proxy + LOG(("FTP:(%p) Configured to use a SOCKS proxy channel\n", this)); + mChannel->SetProxyInfo(pi); + } + } + + if (mDeferredCallbackPending) { + mDeferredCallbackPending = false; + OnCallbackPending(); + } + return NS_OK; +} + void nsFtpState::OnCallbackPending() { @@ -2163,6 +2244,11 @@ nsFtpState::OnCallbackPending() // connect to the server. if (mState == FTP_INIT) { + if (mProxyRequest) { + mDeferredCallbackPending = true; + return; + } + if (CheckCache()) { mState = FTP_WAIT_CACHE; return; diff --git a/netwerk/protocol/ftp/nsFtpConnectionThread.h b/netwerk/protocol/ftp/nsFtpConnectionThread.h index ffa66b249dc8..262c2ffb8172 100644 --- a/netwerk/protocol/ftp/nsFtpConnectionThread.h +++ b/netwerk/protocol/ftp/nsFtpConnectionThread.h @@ -35,6 +35,7 @@ #include "nsICacheEntryDescriptor.h" #include "nsICacheListener.h" +#include "nsIProtocolProxyCallback.h" // ftp server types #define FTP_GENERIC_TYPE 0 @@ -77,6 +78,7 @@ typedef enum _FTP_STATE { typedef enum _FTP_ACTION {GET, PUT} FTP_ACTION; class nsFtpChannel; +class nsICancelable; // The nsFtpState object is the content stream for the channel. It implements // nsIInputStreamCallback, so it can read data from the control connection. It @@ -88,13 +90,16 @@ class nsFtpState : public nsBaseContentStream, public nsITransportEventSink, public nsICacheListener, public nsIRequestObserver, - public nsFtpControlConnectionListener { + public nsFtpControlConnectionListener, + public nsIProtocolProxyCallback +{ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIINPUTSTREAMCALLBACK NS_DECL_NSITRANSPORTEVENTSINK NS_DECL_NSICACHELISTENER NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSIPROTOCOLPROXYCALLBACK // Override input stream methods: NS_IMETHOD CloseWithStatus(nsresult status); @@ -263,6 +268,9 @@ private: bool mDoomCache; nsCString mSuppliedEntityID; + + nsCOMPtr mProxyRequest; + bool mDeferredCallbackPending; }; #endif //__nsFtpState__h_ diff --git a/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp index 5e0aeb4f410f..d6605ce36b28 100644 --- a/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp +++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp @@ -211,11 +211,13 @@ nsFtpProtocolHandler::NewURI(const nsACString &aSpec, NS_IMETHODIMP nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result) { - return NewProxiedChannel(url, nullptr, result); + return NewProxiedChannel(url, nullptr, 0, nullptr, result); } NS_IMETHODIMP nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo, + uint32_t proxyResolveFlags, + nsIURI *proxyURI, nsIChannel* *result) { NS_ENSURE_ARG_POINTER(uri); diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index c28ebc862e6c..7355faad0028 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -50,6 +50,7 @@ HttpBaseChannel::HttpBaseChannel() , mAllowSpdy(true) , mPrivateBrowsing(false) , mSuspendCount(0) + , mProxyResolveFlags(0) { LOG(("Creating HttpBaseChannel @%x\n", this)); @@ -74,7 +75,9 @@ HttpBaseChannel::~HttpBaseChannel() nsresult HttpBaseChannel::Init(nsIURI *aURI, uint8_t aCaps, - nsProxyInfo *aProxyInfo) + nsProxyInfo *aProxyInfo, + uint32_t aProxyResolveFlags, + nsIURI *aProxyURI) { LOG(("HttpBaseChannel::Init [this=%p]\n", this)); @@ -87,6 +90,8 @@ HttpBaseChannel::Init(nsIURI *aURI, mOriginalURI = aURI; mDocumentURI = nullptr; mCaps = aCaps; + mProxyResolveFlags = aProxyResolveFlags; + mProxyURI = aProxyURI; // Construct connection info object nsAutoCString host; @@ -112,11 +117,6 @@ HttpBaseChannel::Init(nsIURI *aURI, if (NS_FAILED(rv)) return rv; LOG(("uri=%s\n", mSpec.get())); - mConnectionInfo = new nsHttpConnectionInfo(host, port, - aProxyInfo, usingSSL); - if (!mConnectionInfo) - return NS_ERROR_OUT_OF_MEMORY; - // Set default request method mRequestHead.SetMethod(nsHttp::Get); @@ -128,8 +128,13 @@ HttpBaseChannel::Init(nsIURI *aURI, rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); if (NS_FAILED(rv)) return rv; - rv = gHttpHandler-> - AddStandardRequestHeaders(&mRequestHead.Headers(), aCaps); + rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead.Headers()); + if (NS_FAILED(rv)) return rv; + + nsAutoCString type; + if (aProxyInfo && NS_SUCCEEDED(aProxyInfo->GetType(type)) && + !type.EqualsLiteral("unknown")) + mProxyInfo = aProxyInfo; return rv; } @@ -1522,7 +1527,9 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, // set, then allow the flag to apply to the redirected channel as well. // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels, // we only need to check if the original channel was using SSL. - if (mConnectionInfo->UsingSSL()) + bool usingSSL = false; + nsresult rv = mURI->SchemeIs("https", &usingSSL); + if (NS_SUCCEEDED(rv) && usingSSL) newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING; // Do not pass along LOAD_CHECK_OFFLINE_CACHE diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index 971958c122ec..1fa8546d4866 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -60,7 +60,9 @@ public: HttpBaseChannel(); virtual ~HttpBaseChannel(); - virtual nsresult Init(nsIURI *aURI, uint8_t aCaps, nsProxyInfo *aProxyInfo); + virtual nsresult Init(nsIURI *aURI, uint8_t aCaps, nsProxyInfo *aProxyInfo, + uint32_t aProxyResolveFlags, + nsIURI *aProxyURI); // nsIRequest NS_IMETHOD GetName(nsACString& aName); @@ -224,6 +226,7 @@ protected: nsCOMPtr mUploadStream; nsAutoPtr mResponseHead; nsRefPtr mConnectionInfo; + nsCOMPtr mProxyInfo; nsCString mSpec; // ASCII encoded URL spec nsCString mContentTypeHint; @@ -269,6 +272,9 @@ protected: uint32_t mSuspendCount; nsAutoPtr > mRedirectedCachekeys; + + uint32_t mProxyResolveFlags; + nsCOMPtr mProxyURI; }; // Share some code while working around C++'s absurd inability to handle casting diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 99ec61d67867..bfb45ff22154 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -333,21 +333,17 @@ nsHttpChannel::~nsHttpChannel() nsresult nsHttpChannel::Init(nsIURI *uri, uint8_t caps, - nsProxyInfo *proxyInfo) + nsProxyInfo *proxyInfo, + uint32_t proxyResolveFlags, + nsIURI *proxyURI) { - nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo); + nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo, + proxyResolveFlags, proxyURI); if (NS_FAILED(rv)) return rv; LOG(("nsHttpChannel::Init [this=%p]\n", this)); - mAuthProvider = - do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1", - &rv); - if (NS_FAILED(rv)) - return rv; - rv = mAuthProvider->Init(this); - return rv; } //----------------------------------------------------------------------------- @@ -407,13 +403,6 @@ nsHttpChannel::Connect() // Consider opening a TCP connection right away SpeculativeConnect(); - // are we offline? - bool offline = gIOService->IsOffline(); - if (offline) - mLoadFlags |= LOAD_ONLY_FROM_CACHE; - else if (PL_strcmp(mConnectionInfo->ProxyType(), "unknown") == 0) - return ResolveProxy(); // Lazily resolve proxy info - // Don't allow resuming when cache must be used if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) { LOG(("Resuming from cache is not supported yet")); @@ -1532,54 +1521,6 @@ nsHttpChannel::ProxyFailover() return AsyncDoReplaceWithProxy(pi); } -void -nsHttpChannel::HandleAsyncReplaceWithProxy() -{ - NS_PRECONDITION(!mCallOnResume, "How did that happen?"); - - if (mSuspendCount) { - LOG(("Waiting until resume to do async proxy replacement [this=%p]\n", - this)); - mCallOnResume = &nsHttpChannel::HandleAsyncReplaceWithProxy; - return; - } - - nsresult status = mStatus; - - nsCOMPtr pi; - pi.swap(mTargetProxyInfo); - if (!mCanceled) { - PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncReplaceWithProxy); - status = AsyncDoReplaceWithProxy(pi); - if (NS_SUCCEEDED(status)) - return; - PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncReplaceWithProxy); - } - - if (NS_FAILED(status)) { - ContinueHandleAsyncReplaceWithProxy(status); - } -} - -nsresult -nsHttpChannel::ContinueHandleAsyncReplaceWithProxy(nsresult status) -{ - if (mLoadGroup && NS_SUCCEEDED(status)) { - mLoadGroup->RemoveRequest(this, nullptr, mStatus); - } - else if (NS_FAILED(status)) { - AsyncAbort(status); - } - - // Return NS_OK here, even it seems to be breaking the async function stack - // contract (i.e. passing the result code to a function bellow). - // ContinueHandleAsyncReplaceWithProxy will always be at the bottom of the - // stack. If we would return the failure code, the async function stack - // logic would cancel the channel synchronously, which is undesired after - // invoking AsyncAbort above. - return NS_OK; -} - void nsHttpChannel::HandleAsyncRedirectChannelToHttps() { @@ -1724,7 +1665,8 @@ nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi) nsresult rv; nsCOMPtr newChannel; - rv = gHttpHandler->NewProxiedChannel(mURI, pi, getter_AddRefs(newChannel)); + rv = gHttpHandler->NewProxiedChannel(mURI, pi, mProxyResolveFlags, + mProxyURI, getter_AddRefs(newChannel)); if (NS_FAILED(rv)) return rv; @@ -1795,11 +1737,16 @@ nsHttpChannel::ResolveProxy() if (NS_FAILED(rv)) return rv; - uint32_t resolveFlags = 0; - if (mConnectionInfo->ProxyInfo()) - mConnectionInfo->ProxyInfo()->GetResolveFlags(&resolveFlags); + // Check if we are configured to directly connect. This will save us + // a round trip through the event dispatch system + uint32_t proxyConfigType; + if (NS_SUCCEEDED(pps->GetProxyConfigType(&proxyConfigType)) && + proxyConfigType == nsIProtocolProxyService::PROXYCONFIG_DIRECT) { + return NS_ERROR_FAILURE; + } - return pps->AsyncResolve(mURI, resolveFlags, this, getter_AddRefs(mProxyRequest)); + return pps->AsyncResolve(mProxyURI ? mProxyURI : mURI, mProxyResolveFlags, + this, getter_AddRefs(mProxyRequest)); } bool @@ -4357,7 +4304,102 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) if (NS_FAILED(rv)) return rv; - if (!(mConnectionInfo && mConnectionInfo->UsingHttpProxy())) { + // Remember the cookie header that was set, if any + const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie); + if (cookieHeader) { + mUserSetCookieHeader = cookieHeader; + } + + AddCookiesToRequest(); + + // notify "http-on-modify-request" observers + gHttpHandler->OnModifyRequest(this); + + mIsPending = true; + mWasOpened = true; + + mListener = listener; + mListenerContext = context; + + // add ourselves to the load group. from this point forward, we'll report + // all failures asynchronously. + if (mLoadGroup) + mLoadGroup->AddRequest(this, nullptr); + + // Collect mAsyncOpenTime after we have called all observers like + // "http-on-modify-request" and load group observers that may set + // mTimingEnabled flag. + if (mTimingEnabled) + mAsyncOpenTime = mozilla::TimeStamp::Now(); + + // are we offline? + bool offline = gIOService->IsOffline(); + if (offline) + mLoadFlags |= LOAD_ONLY_FROM_CACHE; + + // the only time we would already know the proxy information at this + // point would be if we were proxying a non-http protocol like ftp + if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) + return NS_OK; + + return BeginConnect(); +} + +nsresult +nsHttpChannel::BeginConnect() +{ + LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this)); + nsresult rv; + + // Construct connection info object + nsAutoCString host; + int32_t port = -1; + bool usingSSL = false; + + rv = mURI->SchemeIs("https", &usingSSL); + if (NS_SUCCEEDED(rv)) + rv = mURI->GetAsciiHost(host); + if (NS_SUCCEEDED(rv)) + rv = mURI->GetPort(&port); + if (NS_SUCCEEDED(rv)) + rv = mURI->GetAsciiSpec(mSpec); + if (NS_FAILED(rv)) + return rv; + + // Reject the URL if it doesn't specify a host + if (host.IsEmpty()) + return NS_ERROR_MALFORMED_URI; + LOG(("host=%s port=%d\n", host.get(), port)); + LOG(("uri=%s\n", mSpec.get())); + + nsCOMPtr proxyInfo; + if (mProxyInfo) + proxyInfo = do_QueryInterface(mProxyInfo); + + mConnectionInfo = new nsHttpConnectionInfo(host, port, proxyInfo, usingSSL); + + mAuthProvider = + do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1", + &rv); + if (NS_SUCCEEDED(rv)) + rv = mAuthProvider->Init(this); + if (NS_FAILED(rv)) + return rv; + + // check to see if authorization headers should be included + mAuthProvider->AddAuthorizationHeaders(); + + // when proxying only use the pipeline bit if ProxyPipelining() allows it. + if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) { + mCaps &= ~NS_HTTP_ALLOW_PIPELINING; + if (gHttpHandler->ProxyPipelining()) + mCaps |= NS_HTTP_ALLOW_PIPELINING; + } + + // if this somehow fails we can go on without it + gHttpHandler->AddConnectionHeader(&mRequestHead.Headers(), mCaps); + + if (!mConnectionInfo->UsingHttpProxy()) { // Start a DNS lookup very early in case the real open is queued the DNS can // happen in parallel. Do not do so in the presence of an HTTP proxy as // all lookups other than for the proxy itself are done by the proxy. @@ -4373,20 +4415,6 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) mDNSPrefetch->PrefetchHigh(); } - // Remember the cookie header that was set, if any - const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie); - if (cookieHeader) { - mUserSetCookieHeader = cookieHeader; - } - - AddCookiesToRequest(); - - // check to see if authorization headers should be included - mAuthProvider->AddAuthorizationHeaders(); - - // notify "http-on-modify-request" observers - gHttpHandler->OnModifyRequest(this); - // Adjust mCaps according to our request headers: // - If "Connection: close" is set as a request header, then do not bother // trying to establish a keep-alive connection. @@ -4400,23 +4428,6 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) // Force-Reload should reset the persistent connection pool for this host if (mLoadFlags & LOAD_FRESH_CONNECTION) mCaps |= NS_HTTP_CLEAR_KEEPALIVES; - - mIsPending = true; - mWasOpened = true; - - mListener = listener; - mListenerContext = context; - - // add ourselves to the load group. from this point forward, we'll report - // all failures asynchronously. - if (mLoadGroup) - mLoadGroup->AddRequest(this, nullptr); - - // Collect mAsyncOpenTime after we have called all obsrevers like - // "http-on-modify-request" and load group observers that may set - // mTimingEnabled flag. - if (mTimingEnabled) - mAsyncOpenTime = mozilla::TimeStamp::Now(); // We may have been cancelled already, either by on-modify-request // listeners or by load group observers; in that case, we should @@ -4431,14 +4442,10 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) AsyncAbort(rv); } else if (mLoadFlags & LOAD_CLASSIFY_URI) { nsRefPtr classifier = new nsChannelClassifier(); - if (!classifier) { - Cancel(NS_ERROR_OUT_OF_MEMORY); - return NS_OK; - } - rv = classifier->Start(this); if (NS_FAILED(rv)) { Cancel(rv); + return rv; } } @@ -4486,18 +4493,33 @@ NS_IMETHODIMP nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIURI *uri, nsIProxyInfo *pi, nsresult status) { + LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%x mStatus=%x]\n", + this, pi, status, mStatus)); mProxyRequest = nullptr; + nsresult rv; + // If status is a failure code, then it means that we failed to resolve // proxy info. That is a non-fatal error assuming it wasn't because the // request was canceled. We just failover to DIRECT when proxy resolution // fails (failure can mean that the PAC URL could not be loaded). - // Need to replace this channel with a new one. It would be complex to try - // to change the value of mConnectionInfo since so much of our state may - // depend on its state. - mTargetProxyInfo = pi; - HandleAsyncReplaceWithProxy(); + if (NS_SUCCEEDED(status)) + mProxyInfo = pi; + + if (!gHttpHandler->Active()) { + LOG(("nsHttpChannel::OnProxyAvailable [this=%p] " + "Handler no longer active.\n", this)); + rv = NS_ERROR_NOT_AVAILABLE; + } + else { + rv = BeginConnect(); + } + + if (NS_FAILED(rv)) { + Cancel(rv); + DoNotifyListener(); + } return NS_OK; } diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 117a5437e1b9..c579184684da 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -95,7 +95,9 @@ public: nsHttpChannel(); virtual ~nsHttpChannel(); - virtual nsresult Init(nsIURI *aURI, uint8_t aCaps, nsProxyInfo *aProxyInfo); + virtual nsresult Init(nsIURI *aURI, uint8_t aCaps, nsProxyInfo *aProxyInfo, + uint32_t aProxyResolveFlags, + nsIURI *aProxyURI); // Methods HttpBaseChannel didn't implement for us or that we override. // @@ -150,6 +152,7 @@ private: typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result); bool RequestIsConditional(); + nsresult BeginConnect(); nsresult Connect(); nsresult ContinueConnect(); void SpeculativeConnect(); @@ -186,8 +189,6 @@ private: nsresult ProxyFailover(); nsresult AsyncDoReplaceWithProxy(nsIProxyInfo *); nsresult ContinueDoReplaceWithProxy(nsresult); - void HandleAsyncReplaceWithProxy(); - nsresult ContinueHandleAsyncReplaceWithProxy(nsresult); nsresult ResolveProxy(); // cache specific methods @@ -303,9 +304,6 @@ private: // auth specific data nsCOMPtr mAuthProvider; - // Proxy info to replace with - nsCOMPtr mTargetProxyInfo; - // If the channel is associated with a cache, and the URI matched // a fallback namespace, this will hold the key for the fallback // cache entry. diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 874987c94b9c..1c47434c77b7 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -131,9 +131,9 @@ nsHttpHandler::nsHttpHandler() , mHttpVersion(NS_HTTP_VERSION_1_1) , mProxyHttpVersion(NS_HTTP_VERSION_1_1) , mCapabilities(NS_HTTP_ALLOW_KEEPALIVE) - , mProxyCapabilities(NS_HTTP_ALLOW_KEEPALIVE) , mReferrerLevel(0xff) // by default we always send a referrer , mFastFallbackToIPv4(false) + , mProxyPipelining(true) , mIdleTimeout(PR_SecondsToInterval(10)) , mSpdyTimeout(PR_SecondsToInterval(180)) , mMaxRequestAttempts(10) @@ -167,6 +167,7 @@ nsHttpHandler::nsHttpHandler() , mDoNotTrackEnabled(false) , mTelemetryEnabled(false) , mAllowExperiments(true) + , mHandlerActive(false) , mEnableSpdy(false) , mSpdyV2(true) , mSpdyV3(true) @@ -264,6 +265,7 @@ nsHttpHandler::Init() } mSessionStartTime = NowInSeconds(); + mHandlerActive = true; rv = mAuthCache.Init(); if (NS_FAILED(rv)) return rv; @@ -331,8 +333,7 @@ nsHttpHandler::InitConnectionMgr() } nsresult -nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, - uint8_t caps) +nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request) { nsresult rv; @@ -356,6 +357,20 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, rv = request->SetHeader(nsHttp::Accept_Encoding, mAcceptEncodings); if (NS_FAILED(rv)) return rv; + // Add the "Do-Not-Track" header + if (mDoNotTrackEnabled) { + rv = request->SetHeader(nsHttp::DoNotTrack, + NS_LITERAL_CSTRING("1")); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + +nsresult +nsHttpHandler::AddConnectionHeader(nsHttpHeaderArray *request, + uint8_t caps) +{ // RFC2616 section 19.6.2 states that the "Connection: keep-alive" // and "Keep-alive" request headers should not be sent by HTTP/1.1 // user-agents. But this is not a problem in practice, and the @@ -369,13 +384,6 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, connectionType = &keepAlive; } - // Add the "Do-Not-Track" header - if (mDoNotTrackEnabled) { - rv = request->SetHeader(nsHttp::DoNotTrack, - NS_LITERAL_CSTRING("1")); - if (NS_FAILED(rv)) return rv; - } - return request->SetHeader(nsHttp::Connection, *connectionType); } @@ -943,12 +951,8 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) if (PREF_CHANGED(HTTP_PREF("proxy.pipelining"))) { rv = prefs->GetBoolPref(HTTP_PREF("proxy.pipelining"), &cVar); - if (NS_SUCCEEDED(rv)) { - if (cVar) - mProxyCapabilities |= NS_HTTP_ALLOW_PIPELINING; - else - mProxyCapabilities &= ~NS_HTTP_ALLOW_PIPELINING; - } + if (NS_SUCCEEDED(rv)) + mProxyPipelining = cVar; } if (PREF_CHANGED(HTTP_PREF("qos"))) { @@ -1375,7 +1379,7 @@ nsHttpHandler::NewChannel(nsIURI *uri, nsIChannel **result) } } - return NewProxiedChannel(uri, nullptr, result); + return NewProxiedChannel(uri, nullptr, 0, nullptr, result); } NS_IMETHODIMP @@ -1393,6 +1397,8 @@ nsHttpHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) NS_IMETHODIMP nsHttpHandler::NewProxiedChannel(nsIURI *uri, nsIProxyInfo* givenProxyInfo, + uint32_t proxyResolveFlags, + nsIURI *proxyURI, nsIChannel **result) { nsRefPtr httpChannel; @@ -1417,13 +1423,7 @@ nsHttpHandler::NewProxiedChannel(nsIURI *uri, httpChannel = new nsHttpChannel(); } - // select proxy caps if using a non-transparent proxy. SSL tunneling - // should not use proxy settings. - int8_t caps; - if (proxyInfo && !nsCRT::strcmp(proxyInfo->Type(), "http") && !https) - caps = mProxyCapabilities; - else - caps = mCapabilities; + uint8_t caps = mCapabilities; if (https) { // enable pipelining over SSL if requested @@ -1436,7 +1436,7 @@ nsHttpHandler::NewProxiedChannel(nsIURI *uri, } } - rv = httpChannel->Init(uri, caps, proxyInfo); + rv = httpChannel->Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI); if (NS_FAILED(rv)) return rv; @@ -1509,6 +1509,8 @@ nsHttpHandler::Observe(nsISupports *subject, else if (strcmp(topic, "profile-change-net-teardown") == 0 || strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { + mHandlerActive = false; + // clear cache of all authentication credentials. mAuthCache.ClearAll(); diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index fbdd8d4cab94..e4df12542d86 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -58,8 +58,9 @@ public: virtual ~nsHttpHandler(); nsresult Init(); - nsresult AddStandardRequestHeaders(nsHttpHeaderArray *, - uint8_t capabilities); + nsresult AddStandardRequestHeaders(nsHttpHeaderArray *); + nsresult AddConnectionHeader(nsHttpHeaderArray *, + uint8_t capabilities); bool IsAcceptableEncoding(const char *encoding); const nsAFlatCString &UserAgent(); @@ -78,6 +79,7 @@ public: uint8_t GetQoSBits() { return mQoSBits; } uint16_t GetIdleSynTimeout() { return mIdleSynTimeout; } bool FastFallbackToIPv4() { return mFastFallbackToIPv4; } + bool ProxyPipelining() { return mProxyPipelining; } uint32_t MaxSocketCount(); bool EnforceAssocReq() { return mEnforceAssocReq; } @@ -233,6 +235,9 @@ public: mozilla::net::SpdyInformation *SpdyInfo() { return &mSpdyInfo; } + // returns true in between Init and Shutdown states + bool Active() { return mHandlerActive; } + private: // @@ -273,11 +278,10 @@ private: uint8_t mHttpVersion; uint8_t mProxyHttpVersion; uint8_t mCapabilities; - uint8_t mProxyCapabilities; uint8_t mReferrerLevel; bool mFastFallbackToIPv4; - + bool mProxyPipelining; PRIntervalTime mIdleTimeout; PRIntervalTime mSpdyTimeout; @@ -356,6 +360,9 @@ private: // The value of network.allow-experiments bool mAllowExperiments; + // true in between init and shutdown states + bool mHandlerActive; + // Try to use SPDY features instead of HTTP/1.1 over SSL mozilla::net::SpdyInformation mSpdyInfo; bool mEnableSpdy; diff --git a/netwerk/test/unit/test_protocolproxyservice.js b/netwerk/test/unit/test_protocolproxyservice.js index aae2b67b6d14..c5d45acb947b 100644 --- a/netwerk/test/unit/test_protocolproxyservice.js +++ b/netwerk/test/unit/test_protocolproxyservice.js @@ -6,10 +6,21 @@ // This testcase exercises the Protocol Proxy Service +// These are the major sub tests: +// run_filter_test(); +// run_filter_test2() +// run_filter_test3() +// run_pref_test(); +// run_pac_test(); +// run_pac_cancel_test(); +// run_proxy_host_filters_test(); + var ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); var pps = Components.classes["@mozilla.org/network/protocol-proxy-service;1"] .getService(); +var prefs = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); /** * Test nsIProtocolHandler that allows proxying, but doesn't allow HTTP @@ -143,61 +154,109 @@ function run_filter_test() { var uri = ios.newURI("http://www.mozilla.org/", null, null); // Verify initial state + var cb = new resolveCallback(); + cb.nextFunction = filter_test0_1; + var req = pps.asyncResolve(uri, 0, cb); +} - var pi = pps.resolve(uri, 0); +var filter01; +var filter02; + +function filter_test0_1(pi) { do_check_eq(pi, null); // Push a filter and verify the results - var filter1 = new BasicFilter(); - var filter2 = new BasicFilter(); - pps.registerFilter(filter1, 10); - pps.registerFilter(filter2, 20); + filter01 = new BasicFilter(); + filter02 = new BasicFilter(); + pps.registerFilter(filter01, 10); + pps.registerFilter(filter02, 20); - pi = pps.resolve(uri, 0); + var cb = new resolveCallback(); + cb.nextFunction = filter_test0_2; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function filter_test0_2(pi) +{ check_proxy(pi, "http", "localhost", 8080, 0, 10, true); check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false); - pps.unregisterFilter(filter2); - pi = pps.resolve(uri, 0); + pps.unregisterFilter(filter02); + + var cb = new resolveCallback(); + cb.nextFunction = filter_test0_3; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function filter_test0_3(pi) +{ check_proxy(pi, "http", "localhost", 8080, 0, 10, true); check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false); // Remove filter and verify that we return to the initial state - pps.unregisterFilter(filter1); - pi = pps.resolve(uri, 0); - do_check_eq(pi, null); + pps.unregisterFilter(filter01); + + var cb = new resolveCallback(); + cb.nextFunction = filter_test0_4; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); } -function run_filter_test2() { - var uri = ios.newURI("http://www.mozilla.org/", null, null); - - // Verify initial state - - var pi = pps.resolve(uri, 0); +function filter_test0_4(pi) +{ do_check_eq(pi, null); + run_filter_test2(); +} +var filter11; +var filter12; + +function run_filter_test2() { // Push a filter and verify the results - var filter1 = new TestFilter("http", "foo", 8080, 0, 10); - var filter2 = new TestFilter("http", "bar", 8090, 0, 10); - pps.registerFilter(filter1, 20); - pps.registerFilter(filter2, 10); + filter11 = new TestFilter("http", "foo", 8080, 0, 10); + filter12 = new TestFilter("http", "bar", 8090, 0, 10); + pps.registerFilter(filter11, 20); + pps.registerFilter(filter12, 10); - pi = pps.resolve(uri, 0); + var cb = new resolveCallback(); + cb.nextFunction = filter_test1_1; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function filter_test1_1(pi) { check_proxy(pi, "http", "bar", 8090, 0, 10, true); check_proxy(pi.failoverProxy, "http", "foo", 8080, 0, 10, false); - pps.unregisterFilter(filter2); - pi = pps.resolve(uri, 0); + pps.unregisterFilter(filter12); + + var cb = new resolveCallback(); + cb.nextFunction = filter_test1_2; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function filter_test1_2(pi) { check_proxy(pi, "http", "foo", 8080, 0, 10, false); // Remove filter and verify that we return to the initial state - pps.unregisterFilter(filter1); - pi = pps.resolve(uri, 0); + pps.unregisterFilter(filter11); + + var cb = new resolveCallback(); + cb.nextFunction = filter_test1_3; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function filter_test1_3(pi) { do_check_eq(pi, null); + run_filter_test3(); } var filter_3_1; @@ -213,42 +272,56 @@ function run_filter_test3() { var cb = new resolveCallback(); cb.nextFunction = filter_test3_1; var req = pps.asyncResolve(uri, 0, cb); - do_test_pending(); } function filter_test3_1(pi) { check_proxy(pi, "http", "foo", 8080, 0, 10, false); pps.unregisterFilter(filter_3_1); - run_test_continued_3(); - do_test_finished(); + run_pref_test(); } function run_pref_test() { var uri = ios.newURI("http://www.mozilla.org/", null, null); - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - // Verify 'direct' setting prefs.setIntPref("network.proxy.type", 0); - var pi = pps.resolve(uri, 0); + var cb = new resolveCallback(); + cb.nextFunction = pref_test1_1; + var req = pps.asyncResolve(uri, 0, cb); +} + +function pref_test1_1(pi) +{ do_check_eq(pi, null); // Verify 'manual' setting - prefs.setIntPref("network.proxy.type", 1); + var cb = new resolveCallback(); + cb.nextFunction = pref_test1_2; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function pref_test1_2(pi) +{ // nothing yet configured - pi = pps.resolve(uri, 0); do_check_eq(pi, null); // try HTTP configuration prefs.setCharPref("network.proxy.http", "foopy"); prefs.setIntPref("network.proxy.http_port", 8080); - pi = pps.resolve(uri, 0); + var cb = new resolveCallback(); + cb.nextFunction = pref_test1_3; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function pref_test1_3(pi) +{ check_proxy(pi, "http", "foopy", 8080, 0, -1, false); prefs.setCharPref("network.proxy.http", ""); @@ -258,15 +331,33 @@ function run_pref_test() { prefs.setCharPref("network.proxy.socks", "barbar"); prefs.setIntPref("network.proxy.socks_port", 1203); - pi = pps.resolve(uri, 0); + var cb = new resolveCallback(); + cb.nextFunction = pref_test1_4; + var uri = ios.newURI("http://www.mozilla.org/", null, null); + var req = pps.asyncResolve(uri, 0, cb); +} + +function pref_test1_4(pi) +{ check_proxy(pi, "socks", "barbar", 1203, 0, -1, false); + run_pac_test(); } function run_protocol_handler_test() { var uri = ios.newURI("moz-test:foopy", null, null); - var pi = pps.resolve(uri, 0); + var cb = new resolveCallback(); + cb.nextFunction = protocol_handler_test_1; + var req = pps.asyncResolve(uri, 0, cb); +} + +function protocol_handler_test_1(pi) +{ do_check_eq(pi, null); + prefs.setCharPref("network.proxy.autoconfig_url", ""); + prefs.setIntPref("network.proxy.type", 0); + + run_pac_cancel_test(); } function TestResolveCallback() { @@ -292,21 +383,7 @@ TestResolveCallback.prototype = { check_proxy(pi, "http", "foopy", 8080, 0, -1, true); check_proxy(pi.failoverProxy, "direct", "", -1, -1, -1, false); - // verify direct query now that we know the PAC file is loaded - pi = pps.resolve(ios.newURI("http://bazbat.com/", null, null), 0); - do_check_neq(pi, null); - check_proxy(pi, "http", "foopy", 8080, 0, -1, true); - check_proxy(pi.failoverProxy, "direct", "", -1, -1, -1, false); - run_protocol_handler_test(); - - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - prefs.setCharPref("network.proxy.autoconfig_url", ""); - prefs.setIntPref("network.proxy.type", 0); - - run_test_continued(); - do_test_finished(); } }; @@ -317,31 +394,12 @@ function run_pac_test() { '}'; var uri = ios.newURI("http://www.mozilla.org/", null, null); - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - // Configure PAC prefs.setIntPref("network.proxy.type", 2); prefs.setCharPref("network.proxy.autoconfig_url", pac); - // Test it out (we expect an "unknown" result since the PAC load is async) - var pi = pps.resolve(uri, 0); - do_check_neq(pi, null); - do_check_eq(pi.type, "unknown"); - - // We expect the NON_BLOCKING flag to trigger an exception here since - // we have configured the PPS to use PAC. - var hit_exception = false; - try { - pps.resolve(uri, pps.RESOLVE_NON_BLOCKING); - } catch (e) { - hit_exception = true; - } - do_check_eq(hit_exception, true); - var req = pps.asyncResolve(uri, 0, new TestResolveCallback()); - do_test_pending(); } function TestResolveCancelationCallback() { @@ -364,13 +422,10 @@ TestResolveCancelationCallback.prototype = { do_check_eq(status, Components.results.NS_ERROR_ABORT); do_check_eq(pi, null); - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); prefs.setCharPref("network.proxy.autoconfig_url", ""); prefs.setIntPref("network.proxy.type", 0); - run_test_continued_2(); - do_test_finished(); + run_proxy_host_filters_test(); } }; @@ -382,42 +437,70 @@ function run_pac_cancel_test() { 'function FindProxyForURL(url, host) {' + ' return "PROXY foopy:8080; DIRECT";' + '}'; - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); prefs.setIntPref("network.proxy.type", 2); prefs.setCharPref("network.proxy.autoconfig_url", pac); var req = pps.asyncResolve(uri, 0, new TestResolveCancelationCallback()); req.cancel(Components.results.NS_ERROR_ABORT); - do_test_pending(); } -function check_host_filters(hostList, bShouldBeFiltered) { +var hostList; +var hostIDX; +var bShouldBeFiltered; +var hostNextFX; + +function check_host_filters(hl, shouldBe, nextFX) { + hostList = hl; + hostIDX = 0; + bShouldBeFiltered = shouldBe; + hostNextFX = nextFX; + + if (hostList.length > hostIDX) + check_host_filter(hostIDX); +} + +function check_host_filters_cb() +{ + hostIDX++; + if (hostList.length > hostIDX) + check_host_filter(hostIDX); + else + hostNextFX(); +} + +function check_host_filter(i) { var uri; - var proxy; - for (var i=0; i"); do_check_eq(prefs.getCharPref("network.proxy.no_proxies_on"), hostFilterList + ", "); - // Amend lists - move local domain to filtered list uriStrFilterList.push(uriStrUseProxyList.pop()); - check_host_filters(uriStrFilterList, true); - check_host_filters(uriStrUseProxyList, false); + check_host_filters(uriStrFilterList, true, host_filters_3); +} +function host_filters_3() +{ + check_host_filters(uriStrUseProxyList, false, host_filters_4); +} + +function host_filters_4() +{ // Cleanup prefs.setCharPref("network.proxy.no_proxies_on", ""); do_check_eq(prefs.getCharPref("network.proxy.no_proxies_on"), ""); @@ -465,24 +560,44 @@ function run_proxy_host_filters_test() { do_test_finished(); } +function run_deprecated_sync_test() +{ + var uri = ios.newURI("http://www.mozilla.org/", null, null); + + pps.QueryInterface(Components.interfaces.nsIProtocolProxyService2); + + // Verify initial state + var pi = pps.deprecatedBlockingResolve(uri, 0); + do_check_eq(pi, null); + + // Push a filter and verify the results + var filter1 = new BasicFilter(); + var filter2 = new BasicFilter(); + pps.registerFilter(filter1, 10); + pps.registerFilter(filter2, 20); + + pi = pps.deprecatedBlockingResolve(uri, 0); + check_proxy(pi, "http", "localhost", 8080, 0, 10, true); + check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false); + + pps.unregisterFilter(filter2); + pi = pps.deprecatedBlockingResolve(uri, 0); + check_proxy(pi, "http", "localhost", 8080, 0, 10, true); + check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false); + + // Remove filter and verify that we return to the initial state + pps.unregisterFilter(filter1); + pi = pps.deprecatedBlockingResolve(uri, 0); + do_check_eq(pi, null); +} + function run_test() { register_test_protocol_handler(); + + // any synchronous tests + run_deprecated_sync_test(); + + // start of asynchronous test chain run_filter_test(); - run_filter_test2(); - run_pref_test(); - run_pac_test(); - // additional tests may be added to run_test_continued -} - -function run_test_continued() { - run_pac_cancel_test(); - // additional tests may be added to run_test_continued_3 -} - -function run_test_continued_2() { - run_filter_test3(); -} - -function run_test_continued_3() { - run_proxy_host_filters_test(); + do_test_pending(); } diff --git a/netwerk/test/unit/test_proxy-failover_canceled.js b/netwerk/test/unit/test_proxy-failover_canceled.js index 6d02380a5fb9..24a8c73a4d4b 100644 --- a/netwerk/test/unit/test_proxy-failover_canceled.js +++ b/netwerk/test/unit/test_proxy-failover_canceled.js @@ -33,24 +33,11 @@ function run_test() httpServer.registerPathHandler("/content", contentHandler); httpServer.start(4444); + // we want to cancel the failover proxy engage, so, do not allow + // redirects from now. + var nc = new ChannelEventSink(); - var on_modify_request_count = 0; - - modifyrequestobserver = {observe: function() { - // We get 2 on-modify-request notifications: - // 1. when proxy service resolves the proxy settings from PAC function - // 2. when we try to fail over the first proxy (moving to the second one) - // - // In the second case we want to cancel the proxy engage, so, do not allow - // redirects from now. - - if (++on_modify_request_count == 2) - nc._flags = ES_ABORT_REDIRECT; - }} - - var os = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - os.addObserver(modifyrequestobserver, "http-on-modify-request", false); + nc._flags = ES_ABORT_REDIRECT; var prefserv = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefService); diff --git a/netwerk/test/unit/test_proxy-replace_canceled.js b/netwerk/test/unit/test_proxy-replace_canceled.js index 7768e7c42684..bafb57d0c5c7 100644 --- a/netwerk/test/unit/test_proxy-replace_canceled.js +++ b/netwerk/test/unit/test_proxy-replace_canceled.js @@ -41,8 +41,16 @@ function run_test() "function FindProxyForURL(url, host) {return 'PROXY localhost:4444';}" ); + // this test assumed that a AsyncOnChannelRedirect query is made for + // each proxy failover or on the inital proxy only when PAC mode is used. + // Neither of those are documented anywhere that I can find and the latter + // hasn't been a useful property because it is PAC dependent and the type + // is generally unknown and OS driven. 769764 changed that to remove the + // internal redirect used to setup the initial proxy/channel as that isn't + // a redirect in any sense. + var chan = make_channel("http://localhost:4444/content"); - chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT); chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null); + chan.cancel(Cr.NS_BINDING_ABORTED); do_test_pending(); } diff --git a/toolkit/components/passwordmgr/test/test_bug_627616.html b/toolkit/components/passwordmgr/test/test_bug_627616.html index 7b498bfdfac5..c0e7eaeeb893 100644 --- a/toolkit/components/passwordmgr/test/test_bug_627616.html +++ b/toolkit/components/passwordmgr/test/test_bug_627616.html @@ -15,13 +15,33 @@ testNum = 1; var login, login2; - function init() { + + var resolveCallback = { + + QueryInterface : function (iid) { + const interfaces = [Ci.nsIProtocolProxyCallback, Ci.nsISupports]; + + if (!interfaces.some( function(v) { return iid.equals(v) } )) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + }, + + onProxyAvailable : function (req, uri, pi, status) { + init2(SpecialPowers.wrap(pi).host, SpecialPowers.wrap(pi).port); + } + }; + + function init1() { var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(); var uri = ios.newURI("http://example.com", null, null); - var pi = pps.resolve(uri, 0); - var mozproxy = "moz-proxy://" + pi.host + ":" + pi.port; + pps.asyncResolve(uri, 0, resolveCallback); + } + + function init2(proxyHost, proxyPort) { + + var mozproxy = "moz-proxy://" + proxyHost + ":" + proxyPort; var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo); @@ -96,7 +116,7 @@ var pendingTests = [{expectedDialogs: 2, test: testNonAnonymousCredentials}, {expectedDialogs: 1, test: testAnonymousCredentials}, {expectedDialogs: 0, test: testAnonymousNoAuth}]; - init(); + init1(); runNextTest(); function handleDialog(doc, testNum) diff --git a/toolkit/components/passwordmgr/test/test_prompt.html b/toolkit/components/passwordmgr/test/test_prompt.html index 3517865385bd..26ce43c5d47a 100644 --- a/toolkit/components/passwordmgr/test/test_prompt.html +++ b/toolkit/components/passwordmgr/test/test_prompt.html @@ -23,19 +23,16 @@ Login Manager test: username/password prompts var pwmgr, ioService var tmplogin, login1, login2A, login2B, login2C, login2D, login2E, login3A, login3B, login4, proxyLogin; var mozproxy, proxiedHost = "http://mochi.test:8888"; +var testNum = 1; -function initLogins() { +function initLogins(pi) { pwmgr = Cc["@mozilla.org/login-manager;1"]. getService(Ci.nsILoginManager); ioService = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); - // Figure out what our proxy is set to -- can't just hardcode this, because - // mobile platforms don't use localhost. - var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(); - var uri = ioService.newURI(proxiedHost, null, null); - var pi = pps.resolve(uri, 0); - mozproxy = "moz-proxy://" + pi.host + ":" + pi.port; + mozproxy = "moz-proxy://" + SpecialPowers.wrap(pi).host + ":" + + SpecialPowers.wrap(pi).port; tmpLogin = Cc["@mozilla.org/login-manager/loginInfo;1"]. createInstance(Ci.nsILoginInfo); @@ -124,6 +121,35 @@ ok(true, "whee, done!"); SimpleTest.finish(); } +var resolveCallback = { +QueryInterface : function (iid) { +const interfaces = [Ci.nsIProtocolProxyCallback, Ci.nsISupports]; + + if (!interfaces.some( function(v) { return iid.equals(v) } )) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; +}, + + onProxyAvailable : function (req, uri, pi, status) { + initLogins(pi); + doTests(); + } +}; + +function startup() { + //need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy. + var ios = SpecialPowers.wrap(Components) + .classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + + var pps = SpecialPowers.wrap(Components) + .classes["@mozilla.org/network/protocol-proxy-service;1"] + .getService(); + + var uri = ios.newURI("http://example.com", null, null); + pps.asyncResolve(uri, 0, resolveCallback); +} + function addNotificationCallback(cb) { storageObserver.notificationCallbacks.push(cb); @@ -569,8 +595,9 @@ dumpNotifications(); } -initLogins(); +startup(); +function doTests() { var authinfo = { username : "", password : "", @@ -624,7 +651,7 @@ var popupNotifications = getPopupNotifications(window.top); ok(popupNotifications, "Got popupNotifications"); // ===== test 1 ===== -var testNum = 1; +testNum = 1; startCallbackTimer(); isOk = prompter1.prompt(dialogTitle(), dialogText, "http://example.com", Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, "abc", result); @@ -1136,6 +1163,7 @@ iframe.src = "authenticate.sjs?user=mochiuser1&pass=mochipass1"; // ...remaining tests are driven by handleLoad()... SimpleTest.waitForExplicitFinish(); +} diff --git a/toolkit/components/passwordmgr/test/test_prompt_async.html b/toolkit/components/passwordmgr/test/test_prompt_async.html index a7298c4ff2ac..37ef926a99b8 100644 --- a/toolkit/components/passwordmgr/test/test_prompt_async.html +++ b/toolkit/components/passwordmgr/test/test_prompt_async.html @@ -69,7 +69,7 @@ var pwmgr, logins = []; - function initLogins() { + function initLogins(pi) { pwmgr = SpecialPowers.wrap(Components) .classes["@mozilla.org/login-manager;1"] .getService(Ci.nsILoginManager); @@ -83,18 +83,9 @@ logins.push(login); } - //need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy. - var ios = SpecialPowers.wrap(Components) - .classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - - var pps = SpecialPowers.wrap(Components) - .classes["@mozilla.org/network/protocol-proxy-service;1"] - .getService(); - - var uri = ios.newURI("http://example.com", null, null); - var pi = pps.resolve(uri, 0); - var mozproxy = "moz-proxy://" + pi.host + ":" + pi.port; + var mozproxy = "moz-proxy://" + + SpecialPowers.wrap(pi).host + ":" + + SpecialPowers.wrap(pi).port; addLogin(mozproxy, "proxy_realm", "proxy_user", "proxy_pass"); @@ -134,6 +125,34 @@ SimpleTest.finish(); } + var resolveCallback = { + QueryInterface : function (iid) { + const interfaces = [Ci.nsIProtocolProxyCallback, Ci.nsISupports]; + + if (!interfaces.some( function(v) { return iid.equals(v) } )) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + }, + + onProxyAvailable : function (req, uri, pi, status) { + initLogins(pi); + doTest(testNum); + } + }; + + function startup() { + //need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy. + var ios = SpecialPowers.wrap(Components) + .classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + + var pps = SpecialPowers.wrap(Components) + .classes["@mozilla.org/network/protocol-proxy-service;1"] + .getService(); + + var uri = ios.newURI("http://example.com", null, null); + pps.asyncResolve(uri, 0, resolveCallback); + } // --------------- Test loop spin ---------------- var testNum = 1; @@ -148,8 +167,7 @@ iframe2a.onload = onFrameLoad; iframe2b.onload = onFrameLoad; - initLogins(); - doTest(testNum); + startup(); } var expectedLoads; diff --git a/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm index a725bf211cee..8c66003c2388 100644 --- a/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm +++ b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm @@ -34,7 +34,7 @@ public: nsresult GetAutoconfigURL(nsAutoCString& aResult) const; // Find the SystemConfiguration proxy & port for a given URI - nsresult FindSCProxyPort(nsIURI* aURI, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy); + nsresult FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy); // is host:port on the proxy exception list? bool IsInExceptionList(const nsACString& aHost) const; @@ -58,7 +58,14 @@ private: static const SchemeMapping gSchemeMappingList[]; }; -NS_IMPL_ISUPPORTS1(nsOSXSystemProxySettings, nsISystemProxySettings) +NS_IMPL_THREADSAFE_ISUPPORTS1(nsOSXSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsOSXSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + *aMainThreadOnly = false; + return NS_OK; +} // Mapping of URI schemes to SystemConfiguration keys const nsOSXSystemProxySettings::SchemeMapping nsOSXSystemProxySettings::gSchemeMappingList[] = { @@ -156,7 +163,7 @@ nsOSXSystemProxySettings::ProxyHasChanged() } nsresult -nsOSXSystemProxySettings::FindSCProxyPort(nsIURI* aURI, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy) +nsOSXSystemProxySettings::FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -164,8 +171,8 @@ nsOSXSystemProxySettings::FindSCProxyPort(nsIURI* aURI, nsACString& aResultHost, for (const SchemeMapping* keys = gSchemeMappingList; keys->mScheme != NULL; ++keys) { // Check for matching scheme (when appropriate) - bool res; - if ((NS_FAILED(aURI->SchemeIs(keys->mScheme, &res)) || !res) && !keys->mIsSocksProxy) + if (strcasecmp(keys->mScheme, PromiseFlatCString(aScheme).get()) && + !keys->mIsSocksProxy) continue; // Check the proxy is enabled @@ -303,20 +310,20 @@ nsOSXSystemProxySettings::GetPACURI(nsACString& aResult) } nsresult -nsOSXSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) +nsOSXSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - nsAutoCString host; - nsresult rv = aURI->GetHost(host); - NS_ENSURE_SUCCESS(rv, rv); - int32_t proxyPort; nsAutoCString proxyHost; bool proxySocks; - rv = FindSCProxyPort(aURI, proxyHost, proxyPort, proxySocks); + nsresult rv = FindSCProxyPort(aScheme, proxyHost, proxyPort, proxySocks); - if (NS_FAILED(rv) || IsInExceptionList(host)) { + if (NS_FAILED(rv) || IsInExceptionList(aHost)) { aResult.AssignLiteral("DIRECT"); } else if (proxySocks) { aResult.Assign(NS_LITERAL_CSTRING("SOCKS ") + proxyHost + nsPrintfCString(":%d", proxyPort)); diff --git a/toolkit/system/unixproxy/nsLibProxySettings.cpp b/toolkit/system/unixproxy/nsLibProxySettings.cpp index c6b181b2a138..76137b818fb6 100644 --- a/toolkit/system/unixproxy/nsLibProxySettings.cpp +++ b/toolkit/system/unixproxy/nsLibProxySettings.cpp @@ -6,7 +6,6 @@ #include "nsISystemProxySettings.h" #include "mozilla/ModuleUtils.h" #include "nsIServiceManager.h" -#include "nsIIOService.h" #include "nsIURI.h" #include "nsString.h" #include "nsNetUtil.h" @@ -34,7 +33,14 @@ private: pxProxyFactory *mProxyFactory; }; -NS_IMPL_ISUPPORTS1(nsUnixSystemProxySettings, nsISystemProxySettings) +NS_IMPL_THREADSAFE_ISUPPORTS1(nsUnixSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsUnixSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + *aMainThreadOnly = false; + return NS_OK; +} nsresult nsUnixSystemProxySettings::Init() @@ -51,7 +57,11 @@ nsUnixSystemProxySettings::GetPACURI(nsACString& aResult) } nsresult -nsUnixSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) +nsUnixSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) { nsresult rv; @@ -60,15 +70,9 @@ nsUnixSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) } NS_ENSURE_TRUE(mProxyFactory, NS_ERROR_NOT_AVAILABLE); - nsCOMPtr ios = do_GetIOService(&rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString spec; - rv = aURI->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - char **proxyArray = nullptr; - proxyArray = px_proxy_factory_get_proxies(mProxyFactory, (char*)(spec.get())); + proxyArray = px_proxy_factory_get_proxies(mProxyFactory, + PromiseFlatCString(aSpec).get()); NS_ENSURE_TRUE(proxyArray, NS_ERROR_NOT_AVAILABLE); // Translate libproxy's output to PAC string as expected @@ -78,45 +82,34 @@ nsUnixSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) // direct:// // // PAC format: "PROXY proxy1.foo.com:8080; PROXY proxy2.foo.com:8080; DIRECT" + // but nsISystemProxySettings allows "PROXY http://proxy.foo.com:8080" as well. + int c = 0; while (proxyArray[c] != NULL) { if (!aResult.IsEmpty()) { aResult.AppendLiteral("; "); } - bool isScheme = false; - nsXPIDLCString schemeString; - nsXPIDLCString hostPortString; - nsCOMPtr proxyURI; - - rv = ios->NewURI(nsDependentCString(proxyArray[c]), - nullptr, - nullptr, - getter_AddRefs(proxyURI)); - if (NS_FAILED(rv)) { + // figure out the scheme, and we can't use nsIIOService::NewURI because + // this is not the main thread. + char *colon = strchr (proxyArray[c], ':'); + uint32_t schemelen = colon ? colon - proxyArray[c] : 0; + if (schemelen < 1) { c++; continue; } - proxyURI->GetScheme(schemeString); - if (NS_SUCCEEDED(proxyURI->SchemeIs("http", &isScheme)) && isScheme) { - schemeString.AssignLiteral("proxy"); + if (schemelen == 6 && !strncasecmp(proxyArray[c], "direct", 6)) { + aResult.AppendLiteral("DIRECT"); } - aResult.Append(schemeString); - if (NS_SUCCEEDED(proxyURI->SchemeIs("direct", &isScheme)) && !isScheme) { - // Add the proxy URI only if it's not DIRECT - proxyURI->GetHostPort(hostPortString); - aResult.AppendLiteral(" "); - aResult.Append(hostPortString); + else { + aResult.AppendLiteral("PROXY "); + aResult.Append(proxyArray[c]); } c++; } -#ifdef DEBUG - printf("returned PAC proxy string: %s\n", PromiseFlatCString(aResult).get()); -#endif - PR_Free(proxyArray); return NS_OK; } diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp index 97d058cbec62..6322768a5618 100644 --- a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp +++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp @@ -18,6 +18,7 @@ #include "nsIGSettingsService.h" #include "nsInterfaceHashtable.h" #include "mozilla/Attributes.h" +#include "nsIURI.h" class nsUnixSystemProxySettings MOZ_FINAL : public nsISystemProxySettings { public: @@ -43,6 +44,14 @@ private: NS_IMPL_ISUPPORTS1(nsUnixSystemProxySettings, nsISystemProxySettings) +NS_IMETHODIMP +nsUnixSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + // dbus prevents us from being threadsafe, but this routine should not block anyhow + *aMainThreadOnly = true; + return NS_OK; +} + nsresult nsUnixSystemProxySettings::Init() { @@ -151,8 +160,10 @@ static void SetProxyResult(const char* aType, const nsACString& aHost, aResult.AppendASCII(aType); aResult.Append(' '); aResult.Append(aHost); - aResult.Append(':'); - aResult.Append(nsPrintfCString("%d", aPort)); + if (aPort > 0) { + aResult.Append(':'); + aResult.Append(nsPrintfCString("%d", aPort)); + } } static nsresult @@ -481,29 +492,21 @@ nsUnixSystemProxySettings::GetProxyFromGSettings(const nsACString& aScheme, } nsresult -nsUnixSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) +nsUnixSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) { - nsAutoCString scheme; - nsresult rv = aURI->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString host; - rv = aURI->GetHost(host); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t port; - rv = aURI->GetPort(&port); - NS_ENSURE_SUCCESS(rv, rv); - if (mProxySettings) { - rv = GetProxyFromGSettings(scheme, host, port, aResult); - if (rv == NS_OK) + nsresult rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult); + if (NS_SUCCEEDED(rv)) return rv; } if (mGConf) - return GetProxyFromGConf(scheme, host, port, aResult); + return GetProxyFromGConf(aScheme, aHost, aPort, aResult); - return GetProxyFromEnvironment(scheme, host, port, aResult); + return GetProxyFromEnvironment(aScheme, aHost, aPort, aResult); } #define NS_UNIXSYSTEMPROXYSERVICE_CID /* 0fa3158c-d5a7-43de-9181-a285e74cf1d4 */\ diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp index bf379ff55ef9..87c133f3488c 100644 --- a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp +++ b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp @@ -32,7 +32,15 @@ private: bool PatternMatch(const nsACString& aHost, const nsACString& aOverride); }; -NS_IMPL_ISUPPORTS1(nsWindowsSystemProxySettings, nsISystemProxySettings) +NS_IMPL_THREADSAFE_ISUPPORTS1(nsWindowsSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsWindowsSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + *aMainThreadOnly = false; + return NS_OK; +} + nsresult nsWindowsSystemProxySettings::Init() @@ -40,33 +48,12 @@ nsWindowsSystemProxySettings::Init() return NS_OK; } -static void SetProxyResult(const char* aType, const nsACString& aHost, - int32_t aPort, nsACString& aResult) -{ - aResult.AssignASCII(aType); - aResult.Append(' '); - aResult.Append(aHost); - aResult.Append(':'); - aResult.Append(nsPrintfCString("%d", aPort)); -} - static void SetProxyResult(const char* aType, const nsACString& aHostPort, nsACString& aResult) { - nsCOMPtr uri; - nsAutoCString host; - int32_t port; - - // Try parsing it as a URI. - if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), aHostPort)) && - NS_SUCCEEDED(uri->GetHost(host)) && !host.IsEmpty() && - NS_SUCCEEDED(uri->GetPort(&port))) { - SetProxyResult(aType, host, port, aResult); - } else { - aResult.AssignASCII(aType); - aResult.Append(' '); - aResult.Append(aHostPort); - } + aResult.AssignASCII(aType); + aResult.Append(' '); + aResult.Append(aHostPort); } static void SetProxyResultDirect(nsACString& aResult) @@ -222,7 +209,11 @@ nsWindowsSystemProxySettings::GetPACURI(nsACString& aResult) } nsresult -nsWindowsSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) +nsWindowsSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) { nsresult rv; uint32_t flags = 0; @@ -234,15 +225,7 @@ nsWindowsSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) return NS_OK; } - nsAutoCString scheme; - rv = aURI->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString host; - rv = aURI->GetHost(host); - NS_ENSURE_SUCCESS(rv, rv); - - if (MatchOverride(host)) { + if (MatchOverride(aHost)) { SetProxyResultDirect(aResult); return NS_OK; } @@ -250,7 +233,7 @@ nsWindowsSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult) NS_ConvertUTF16toUTF8 cbuf(buf); nsAutoCString prefix; - ToLowerCase(scheme, prefix); + ToLowerCase(aScheme, prefix); prefix.Append('=');