aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Kristina Chodorow <kchodorow@google.com>2015-12-22 19:21:21 +0000
committerGravatar Dmitry Lomov <dslomov@google.com>2015-12-22 19:57:48 +0000
commit5fd83b28a267bbd4edbd01370e4b7905f5f9b0de (patch)
tree35953c905336be9cff614deb6b702553d20f31cc
parent3a837477b79599263dd0f6284806aed291c7dcfd (diff)
Add support for downloading through proxies
Fixes #587. -- MOS_MIGRATED_REVID=110785300
-rw-r--r--site/docs/external.md5
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloader.java52
-rw-r--r--src/test/java/com/google/devtools/build/lib/bazel/repository/HttpDownloaderTest.java91
3 files changed, 147 insertions, 1 deletions
diff --git a/site/docs/external.md b/site/docs/external.md
index 423e6f7c4f..29646ed6a2 100644
--- a/site/docs/external.md
+++ b/site/docs/external.md
@@ -33,6 +33,11 @@ By default, external dependencies are fetched as needed during `bazel build`. If
you would like to disable this behavior or prefetch dependencies, use
[`bazel fetch`](http://bazel.io/docs/bazel-user-manual.html#fetch).
+## Using Proxies
+
+Bazel will pick up proxy addresses from the `HTTPS_PROXY` and `HTTP_PROXY`
+environment variables and use these to download HTTP/HTTPS files (if specified).
+
<a name="transitive-dependencies"></a>
## Transitive dependencies
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloader.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloader.java
index e14edbb8f1..3f1495012f 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloader.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloader.java
@@ -14,6 +14,8 @@
package com.google.devtools.build.lib.bazel.repository;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.devtools.build.lib.events.Event;
@@ -30,6 +32,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -175,12 +179,58 @@ public class HttpDownloader {
}
private InputStream getInputStream(URL url) throws IOException {
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection(
+ createProxyIfNeeded(url.getProtocol()));
connection.setInstanceFollowRedirects(true);
connection.connect();
return connection.getInputStream();
}
+ private static Proxy createProxyIfNeeded(String protocol) throws IOException {
+ if (protocol.equals("https")) {
+ return createProxy(System.getenv("HTTPS_PROXY"));
+ } else if (protocol.equals("http")) {
+ return createProxy(System.getenv("HTTP_PROXY"));
+ }
+ return Proxy.NO_PROXY;
+ }
+
+ @VisibleForTesting
+ static Proxy createProxy(String proxyAddress) throws IOException {
+ if (Strings.isNullOrEmpty(proxyAddress)) {
+ return Proxy.NO_PROXY;
+ }
+
+ // Split the proxyAddress into the protocol, address, and optional port. This does not use the
+ // URL class to avoid the DNS resolution it performs on creation.
+ int protocolIndex = proxyAddress.indexOf(':');
+ if (protocolIndex == -1) {
+ throw new IOException("No proxy protocol found for " + proxyAddress);
+ }
+ boolean https;
+ String protocol = proxyAddress.substring(0, protocolIndex);
+ if (protocol.equals("https")) {
+ https = true;
+ } else if (protocol.equals("http")) {
+ https = false;
+ } else {
+ throw new IOException("Invalid proxy protocol for " + proxyAddress);
+ }
+ int portIndex = proxyAddress.lastIndexOf(':');
+ if (protocolIndex == portIndex) {
+ // No port set, just the http: colon.
+ return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress, https ? 443 : 80));
+ }
+
+ try {
+ return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(
+ proxyAddress.substring(0, portIndex),
+ Integer.parseInt(proxyAddress.substring(portIndex + 1))));
+ } catch (NumberFormatException e) {
+ throw new IOException("Error parsing proxy port: " + proxyAddress);
+ }
+ }
+
public static String getHash(Hasher hasher, Path path) throws IOException {
byte byteBuffer[] = new byte[BUFFER_SIZE];
try (InputStream stream = path.getInputStream()) {
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/repository/HttpDownloaderTest.java b/src/test/java/com/google/devtools/build/lib/bazel/repository/HttpDownloaderTest.java
new file mode 100644
index 0000000000..93afe43516
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/bazel/repository/HttpDownloaderTest.java
@@ -0,0 +1,91 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel.repository;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.net.Proxy;
+
+/**
+ * Tests for @{link HttpDownloader}.
+ */
+@RunWith(JUnit4.class)
+public class HttpDownloaderTest {
+ @Test
+ public void testNoProxy() throws Exception {
+ // Empty address.
+ Proxy proxy = HttpDownloader.createProxy(null);
+ assertEquals(Proxy.NO_PROXY, proxy);
+ proxy = HttpDownloader.createProxy("");
+ assertEquals(Proxy.NO_PROXY, proxy);
+ }
+
+ @Test
+ public void testProxyDefaultPort() throws Exception {
+ Proxy proxy = HttpDownloader.createProxy("http://my.example.com");
+ assertEquals(Proxy.Type.HTTP, proxy.type());
+ assertThat(proxy.toString()).endsWith(":80");
+
+ proxy = HttpDownloader.createProxy("https://my.example.com");
+ assertThat(proxy.toString()).endsWith(":443");
+ }
+
+ @Test
+ public void testProxyExplicitPort() throws Exception {
+ Proxy proxy = HttpDownloader.createProxy("http://my.example.com:12345");
+ assertThat(proxy.toString()).endsWith(":12345");
+
+ proxy = HttpDownloader.createProxy("https://my.example.com:12345");
+ assertThat(proxy.toString()).endsWith(":12345");
+ }
+
+ @Test
+ public void testProxyNoProtocol() throws Exception {
+ try {
+ HttpDownloader.createProxy("my.example.com");
+ fail("Expected protocol error");
+ } catch (IOException e) {
+ assertThat(e.getMessage()).contains("No proxy protocol found");
+ }
+ }
+
+ @Test
+ public void testProxyNoProtocolWithPort() throws Exception {
+ try {
+ HttpDownloader.createProxy("my.example.com:12345");
+ fail("Expected protocol error");
+ } catch (IOException e) {
+ assertThat(e.getMessage()).contains("Invalid proxy protocol");
+ }
+ }
+
+ @Test
+ public void testProxyPortParsingError() throws Exception {
+ try {
+ HttpDownloader.createProxy("http://my.example.com:foo");
+ fail("Should have thrown an error for invalid port");
+ } catch (IOException e) {
+ assertThat(e.getMessage()).contains("Error parsing proxy port");
+ }
+ }
+
+}