Bug 72444: Option to bypass proxy server for local addresses. r=honzab
authorSteve Workman <sjhworkman@gmail.com>
Wed, 21 Sep 2011 15:13:45 -0400
changeset 78606 1d490722d3331e6bdfc17a40beab470410dec36a
parent 78605 682459dc5a1c613782fba559b2566c85f1c61e36
child 78607 4495e1f795c218d562205f3364ac1f84aecee220
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs72444
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 72444: Option to bypass proxy server for local addresses. r=honzab
netwerk/base/src/nsProtocolProxyService.cpp
netwerk/base/src/nsProtocolProxyService.h
netwerk/test/unit/test_protocolproxyservice.js
--- a/netwerk/base/src/nsProtocolProxyService.cpp
+++ b/netwerk/base/src/nsProtocolProxyService.cpp
@@ -301,17 +301,18 @@ NS_IMPL_QUERY_INTERFACE3_CI(nsProtocolPr
                             nsIProtocolProxyService,
                             nsIProtocolProxyService2,
                             nsIObserver)
 NS_IMPL_CI_INTERFACE_GETTER2(nsProtocolProxyService,
                              nsIProtocolProxyService,
                              nsIProtocolProxyService2)
 
 nsProtocolProxyService::nsProtocolProxyService()
-    : mFilters(nsnull)
+    : mFilterLocalHosts(PR_FALSE)
+    , mFilters(nsnull)
     , mProxyConfig(PROXYCONFIG_DIRECT)
     , mHTTPProxyPort(-1)
     , mFTPProxyPort(-1)
     , mHTTPSProxyPort(-1)
     , mSOCKSProxyPort(-1)
     , mSOCKSProxyVersion(4)
     , mSOCKSProxyRemoteDNS(PR_FALSE)
     , mPACMan(nsnull)
@@ -538,16 +539,22 @@ nsProtocolProxyService::CanUseProxy(nsIU
             memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
         }
         else {
             NS_WARNING("unknown address family");
             return PR_TRUE; // allow proxying
         }
     }
     
+    // Don't use proxy for local hosts (plain hostname, no dots)
+    if (!is_ipaddr && mFilterLocalHosts && (kNotFound == host.FindChar('.'))) {
+        LOG(("Not using proxy for this local host [%s]!\n", host.get()));
+        return PR_FALSE; // don't allow proxying
+    }
+
     PRInt32 index = -1;
     while (++index < PRInt32(mHostFiltersArray.Length())) {
         HostInfo *hinfo = mHostFiltersArray[index];
 
         if (is_ipaddr != hinfo->is_ipaddr)
             continue;
         if (hinfo->port && hinfo->port != port)
             continue;
@@ -844,18 +851,20 @@ nsProtocolProxyService::Resolve(nsIURI *
 {
     nsProtocolInfo info;
     nsresult rv = GetProtocolInfo(uri, &info);
     if (NS_FAILED(rv))
         return rv;
 
     PRBool usePAC;
     rv = Resolve_Internal(uri, info, flags, &usePAC, result);
-    if (NS_FAILED(rv))
+    if (NS_FAILED(rv)) {
+        LOG(("Resolve_Internal returned rv(0x%08x)\n", rv));
         return rv;
+    }
 
     if (usePAC && mPACMan) {
         NS_ASSERTION(*result == nsnull, "we should not have a result yet");
 
         // If the caller didn't want us to invoke PAC, then error out.
         if (flags & RESOLVE_NON_BLOCKING)
             return NS_BASE_STREAM_WOULD_BLOCK;
 
@@ -1064,16 +1073,18 @@ nsProtocolProxyService::LoadHostFilters(
 
     if (!filters)
         return; // fail silently...
 
     //
     // filter  = ( host | domain | ipaddr ["/" mask] ) [":" port] 
     // filters = filter *( "," LWS filter)
     //
+    // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref string
+    mFilterLocalHosts = PR_FALSE;
     while (*filters) {
         // skip over spaces and ,
         while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters)))
             filters++;
 
         const char *starthost = filters;
         const char *endhost = filters + 1; // at least that...
         const char *portLocation = 0; 
@@ -1086,28 +1097,37 @@ nsProtocolProxyService::LoadHostFilters(
                 maskLocation = endhost;
             else if (*endhost == ']') // IPv6 address literals
                 portLocation = 0;
             endhost++;
         }
 
         filters = endhost; // advance iterator up front
 
-        HostInfo *hinfo = new HostInfo();
-        if (!hinfo)
-            return; // fail silently
-        hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
-
         // locate end of host
         const char *end = maskLocation ? maskLocation :
                           portLocation ? portLocation :
                           endhost;
 
         nsCAutoString str(starthost, end - starthost);
 
+        // If the current host filter is "<local>", then all local (i.e.
+        // no dots in the hostname) hosts should bypass the proxy
+        if (str.EqualsIgnoreCase("<local>")) {
+            mFilterLocalHosts = PR_TRUE;
+            LOG(("loaded filter for local hosts "
+                 "(plain host names, no dots)\n"));
+            // Continue to next host filter;
+            continue;
+        }
+
+        // For all other host filters, create HostInfo object and add to list
+        HostInfo *hinfo = new HostInfo();
+        hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
+
         PRNetAddr addr;
         if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) {
             hinfo->is_ipaddr   = PR_TRUE;
             hinfo->ip.family   = PR_AF_INET6; // we always store address as IPv6
             hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128;
 
             if (hinfo->ip.mask_len == 0) {
                 NS_WARNING("invalid mask");
--- a/netwerk/base/src/nsProtocolProxyService.h
+++ b/netwerk/base/src/nsProtocolProxyService.h
@@ -348,16 +348,19 @@ protected:
 
       FilterLink(PRUint32 p, nsIProtocolProxyFilter *f)
         : next(nsnull), position(p), filter(f) {}
 
       // Chain deletion to simplify cleaning up the filter links
       ~FilterLink() { if (next) delete next; }
     };
 
+    // Indicates if local hosts (plain hostnames, no dots) should use the proxy
+    PRBool mFilterLocalHosts;
+
     // Holds an array of HostInfo objects
     nsTArray<nsAutoPtr<HostInfo> > mHostFiltersArray;
 
     // Points to the start of a sorted by position, singly linked list
     // of FilterLink objects.
     FilterLink                  *mFilters;
 
     PRUint32                     mProxyConfig;
--- a/netwerk/test/unit/test_protocolproxyservice.js
+++ b/netwerk/test/unit/test_protocolproxyservice.js
@@ -379,24 +379,98 @@ function run_pac_cancel_test() {
   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 uri;
+  var proxy;
+  for (var i=0; i<hostList.length; i++) {
+    dump("*** uri=" + hostList[i] + " bShouldBeFiltered=" + bShouldBeFiltered + "\n");
+    uri = ios.newURI(hostList, null, null);
+    proxy = pps.resolve(uri, 0); 
+    if (bShouldBeFiltered) {
+      do_check_eq(proxy, null);
+    } else {
+      do_check_neq(proxy, null);
+      // Just to be sure, let's check that the proxy is correct
+      // - this should match the proxy setup in the calling function
+      check_proxy(proxy, "http", "foopy", 8080, 0, -1, false);
+    }
+  }
+}
+
+
+// Verify that hists in the host filter list are not proxied
+// refers to "network.proxy.no_proxies_on"
+
+function run_proxy_host_filters_test() {
+  // Get prefs object from DOM
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  // Setup a basic HTTP proxy configuration
+  // - pps.resolve() needs this to return proxy info for non-filtered hosts
+  prefs.setIntPref("network.proxy.type", 1);
+  prefs.setCharPref("network.proxy.http", "foopy");
+  prefs.setIntPref("network.proxy.http_port", 8080);
+
+  // Setup host filter list string for "no_proxies_on"
+  var hostFilterList = "www.mozilla.org, www.google.com, www.apple.com, "
+                       + ".domain, .domain2.org"
+  prefs.setCharPref("network.proxy.no_proxies_on", hostFilterList);
+  do_check_eq(prefs.getCharPref("network.proxy.no_proxies_on"), hostFilterList);
+  
+  var rv;
+  // Check the hosts that should be filtered out
+  var uriStrFilterList = [ "http://www.mozilla.org/",
+                           "http://www.google.com/",
+                           "http://www.apple.com/",
+                           "http://somehost.domain/",
+                           "http://someotherhost.domain/",
+                           "http://somehost.domain2.org/",
+                           "http://somehost.subdomain.domain2.org/" ];
+  check_host_filters(uriStrFilterList, true);
+
+  // Check the hosts that should be proxied
+  var uriStrUseProxyList = [ "http://www.mozilla.com/",
+                             "http://mail.google.com/",
+                             "http://somehost.domain.co.uk/",
+                             "http://somelocalhost/" ];  
+  check_host_filters(uriStrUseProxyList, false);
+  
+  // Set no_proxies_on to include local hosts
+  prefs.setCharPref("network.proxy.no_proxies_on", hostFilterList + ", <local>");
+  do_check_eq(prefs.getCharPref("network.proxy.no_proxies_on"),
+              hostFilterList + ", <local>");
+
+  // Amend lists - move local domain to filtered list
+  uriStrFilterList.push(uriStrUseProxyList.pop());
+  check_host_filters(uriStrFilterList, true);
+  check_host_filters(uriStrUseProxyList, false);
+
+  // Cleanup
+  prefs.setCharPref("network.proxy.no_proxies_on", "");
+  do_check_eq(prefs.getCharPref("network.proxy.no_proxies_on"), "");  
+
+  do_test_finished();
+}
+
 function run_test() {
   register_test_protocol_handler();
   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_2
 }
 
 function run_test_continued_2() {
+  run_proxy_host_filters_test();
 }