Skip to content

Commit

Permalink
[FIXED JENKINS-32326] Support no_proxy environment variable (#84)
Browse files Browse the repository at this point in the history
Change-Id: I894e7831677ee39c37020b0fab0e6db5b290f9ca
  • Loading branch information
etiennebec authored and oleg-nenashev committed Jun 9, 2016
1 parent 59e33a4 commit a1bdc23
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 2 deletions.
53 changes: 51 additions & 2 deletions src/main/java/hudson/remoting/Util.java
Expand Up @@ -107,6 +107,55 @@ static String indent(String s) {
return " " + s.trim().replace("\n", "\n ");
}

/**
* Check if given URL is in the exclusion list defined by the no_proxy environment variable.
* On most *NIX system wildcards are not supported but if one top domain is added, all related subdomains will also
* be ignored. Both "mit.edu" and ".mit.edu" are valid syntax.
* http://www.gnu.org/software/wget/manual/html_node/Proxies.html
*
* Regexp:
* - \Q and \E: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
* - To match IPV4/IPV/FQDN: Regular Expressions Cookbook, 2nd Edition (ISBN: 9781449327453)
*
* Warning: this method won't match shortened representation of IPV6 address
*/
static boolean inNoProxyEnvVar(String host) {
String noProxy = System.getenv("no_proxy");
if (noProxy != null) {
noProxy = noProxy.trim()
// Remove spaces
.replaceAll("\\s+", "")
// Convert .foobar.com to foobar.com
.replaceAll("((?<=^|,)\\.)*(([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,})(?=($|,))", "$2");

if (!noProxy.isEmpty()) {
// IPV4 and IPV6
if (host.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$") || host.matches("^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$")) {
return noProxy.matches(".*(^|,)\\Q" + host + "\\E($|,).*");
}
else {
int depth = 0;
// Loop while we have a valid domain name: acme.com
// We add a safeguard to avoid a case where the host would always be valid because the regex would
// for example fail to remove subdomains.
// According to Wikipedia (no RFC defines it), 128 is the max number of subdivision for a valid FQDN:
// https://en.wikipedia.org/wiki/Subdomain#Overview
while (host.matches("^([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,}$") && depth < 128) {
++depth;
// Check if the no_proxy contains the host
if (noProxy.matches(".*(^|,)\\Q" + host + "\\E($|,).*"))
return true;
// Remove first subdomain: master.jenkins.acme.com -> jenkins.acme.com
else
host = host.replaceFirst("^[a-z0-9]+(-[a-z0-9]+)*\\.", "");
}
}
}
}

return false;
}

/**
* Gets URL connection.
* If http_proxy environment variable exists, the connection uses the proxy.
Expand All @@ -118,7 +167,7 @@ static URLConnection openURLConnection(URL url) throws IOException {
httpProxy = System.getenv("http_proxy");
}
URLConnection con = null;
if (httpProxy != null && "http".equals(url.getProtocol())) {
if (httpProxy != null && "http".equals(url.getProtocol()) && !inNoProxyEnvVar(url.getHost())) {
try {
URL proxyUrl = new URL(httpProxy);
SocketAddress addr = new InetSocketAddress(proxyUrl.getHost(), proxyUrl.getPort());
Expand Down Expand Up @@ -156,7 +205,7 @@ static InetSocketAddress getResolvedHttpProxyAddress(String host, int port) thro
}
if(targetAddress == null) {
String httpProxy = System.getenv("http_proxy");
if(httpProxy != null) {
if(httpProxy != null && !inNoProxyEnvVar(host)) {
try {
URL url = new URL(httpProxy);
targetAddress = new InetSocketAddress(url.getHost(), url.getPort());
Expand Down
134 changes: 134 additions & 0 deletions src/test/java/hudson/remoting/UtilTest.java
@@ -0,0 +1,134 @@
/*
* The MIT License
*
* Copyright (c) 2016, Schneider Electric
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package hudson.remoting;

import junit.framework.TestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

/**
* @author Etienne Bec
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest(Util.class)
public class UtilTest extends TestCase {

@Before
public void mockSystem() {
PowerMockito.mockStatic(System.class);
}

@Test
public void testIPV4() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("10.0.0.1");

assertEquals(true, Util.inNoProxyEnvVar("10.0.0.1"));
}

@Test
public void testWrongIPV4() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("127.0.0.1");

assertEquals(false, Util.inNoProxyEnvVar("10.0.0.1"));
}

@Test
public void testIPV6() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
assertEquals(true, Util.inNoProxyEnvVar("2001:0db8:85a3:0000:0000:8a2e:0370:7334"));
}

@Test
public void testWrongIPV6() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("0:0:0:0:0:0:0:1");

assertEquals(false, Util.inNoProxyEnvVar("2001:0db8:85a3:0000:0000:8a2e:0370:7334"));
}

@Test
public void testFQDN() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("foobar.com");

assertEquals(true, Util.inNoProxyEnvVar("foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.sub.foobar.com"));

assertEquals(false, Util.inNoProxyEnvVar("foobar.org"));
assertEquals(false, Util.inNoProxyEnvVar("jenkins.com"));
}

@Test
public void testSubFQDN() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("sub.foobar.com");

assertEquals(true, Util.inNoProxyEnvVar("sub.foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.sub.foobar.com"));

assertEquals(false, Util.inNoProxyEnvVar("foobar.com"));
}

@Test
public void testFQDNWithDot() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn(".foobar.com");

assertEquals(true, Util.inNoProxyEnvVar("foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.sub.foobar.com"));

assertEquals(false, Util.inNoProxyEnvVar("foobar.org"));
assertEquals(false, Util.inNoProxyEnvVar("jenkins.com"));
}

@Test
public void testSubFQDNWithDot() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn(".sub.foobar.com");

assertEquals(true, Util.inNoProxyEnvVar("sub.foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.sub.foobar.com"));

assertEquals(false, Util.inNoProxyEnvVar("foobar.com"));
}

@Test
public void testMixed() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn(" 127.0.0.1, 0:0:0:0:0:0:0:1,\tfoobar.com, .jenkins.com");

assertEquals(true, Util.inNoProxyEnvVar("127.0.0.1"));
assertEquals(true, Util.inNoProxyEnvVar("0:0:0:0:0:0:0:1"));
assertEquals(true, Util.inNoProxyEnvVar("foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.foobar.com"));
assertEquals(true, Util.inNoProxyEnvVar("sub.jenkins.com"));

assertEquals(false, Util.inNoProxyEnvVar("foobar.org"));
assertEquals(false, Util.inNoProxyEnvVar("jenkins.org"));
assertEquals(false, Util.inNoProxyEnvVar("sub.foobar.org"));
assertEquals(false, Util.inNoProxyEnvVar("sub.jenkins.org"));
}
}

0 comments on commit a1bdc23

Please sign in to comment.