001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.api.utils;
021
022 import com.google.common.base.Joiner;
023 import com.google.common.collect.Lists;
024 import org.apache.commons.configuration.Configuration;
025 import org.apache.commons.configuration.PropertiesConfiguration;
026 import org.apache.commons.io.FileUtils;
027 import org.apache.commons.io.IOUtils;
028 import org.slf4j.LoggerFactory;
029 import org.sonar.api.BatchComponent;
030 import org.sonar.api.ServerComponent;
031 import org.sonar.api.platform.Server;
032
033 import java.io.File;
034 import java.io.FileOutputStream;
035 import java.io.IOException;
036 import java.io.InputStream;
037 import java.net.*;
038 import java.util.List;
039
040 /**
041 * This component downloads HTTP files
042 *
043 * @since 2.2
044 */
045 public class HttpDownloader implements BatchComponent, ServerComponent {
046
047 public static final int TIMEOUT_MILLISECONDS = 20 * 1000;
048
049 private String userAgent;
050
051 public HttpDownloader(Server server, Configuration configuration) {
052 this(configuration, server.getVersion());
053 }
054
055 public HttpDownloader(Configuration configuration) {
056 this(configuration, null);
057 }
058
059 /**
060 * Should be package protected for unit tests only, but public is still required for jacoco 0.2.
061 */
062 public HttpDownloader() {
063 this(new PropertiesConfiguration(), null);
064 }
065
066 private HttpDownloader(Configuration configuration, String userAgent) {
067 initProxy(configuration);
068 initUserAgent(userAgent);
069 }
070
071 private void initProxy(Configuration configuration) {
072 propagateProxySystemProperties(configuration);
073 if (requiresProxyAuthentication(configuration)) {
074 registerProxyCredentials(configuration);
075 }
076 }
077
078 private void initUserAgent(String sonarVersion) {
079 String userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion));
080 System.setProperty("http.agent", userAgent);
081 this.userAgent = userAgent;
082 }
083
084 public String getProxySynthesis(URI uri) {
085 return getProxySynthesis(uri, ProxySelector.getDefault());
086 }
087
088 static String getProxySynthesis(URI uri, ProxySelector proxySelector) {
089 List<String> descriptions = Lists.newArrayList();
090 List<Proxy> proxies = proxySelector.select(uri);
091 if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) {
092 descriptions.add("no proxy");
093 } else {
094 for (Proxy proxy : proxies) {
095 if (!proxy.type().equals(Proxy.Type.DIRECT)) {
096 descriptions.add("proxy: " + proxy.address().toString());
097 }
098 }
099 }
100 return Joiner.on(", ").join(descriptions);
101 }
102
103 private void registerProxyCredentials(Configuration configuration) {
104 Authenticator.setDefault(new ProxyAuthenticator(configuration.getString("http.proxyUser"), configuration
105 .getString("http.proxyPassword")));
106 }
107
108 private boolean requiresProxyAuthentication(Configuration configuration) {
109 return configuration.getString("http.proxyUser") != null;
110 }
111
112 private void propagateProxySystemProperties(Configuration configuration) {
113 propagateSystemProperty(configuration, "http.proxyHost");
114 propagateSystemProperty(configuration, "http.proxyPort");
115 propagateSystemProperty(configuration, "http.nonProxyHosts");
116 propagateSystemProperty(configuration, "http.auth.ntlm.domain");
117 propagateSystemProperty(configuration, "socksProxyHost");
118 propagateSystemProperty(configuration, "socksProxyPort");
119 }
120
121 private void propagateSystemProperty(Configuration configuration, String key) {
122 if (configuration.getString(key) != null) {
123 System.setProperty(key, configuration.getString(key));
124 }
125 }
126
127 public void download(URI uri, File toFile) {
128 InputStream input = null;
129 FileOutputStream output = null;
130 try {
131 HttpURLConnection connection = newHttpConnection(uri);
132 output = new FileOutputStream(toFile, false);
133 input = connection.getInputStream();
134 IOUtils.copy(input, output);
135
136 } catch (Exception e) {
137 IOUtils.closeQuietly(output);
138 FileUtils.deleteQuietly(toFile);
139 throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
140
141 } finally {
142 IOUtils.closeQuietly(input);
143 IOUtils.closeQuietly(output);
144 }
145 }
146
147 public byte[] download(URI uri) {
148 InputStream input = null;
149 try {
150 HttpURLConnection connection = newHttpConnection(uri);
151 input = connection.getInputStream();
152 return IOUtils.toByteArray(input);
153
154 } catch (Exception e) {
155 throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
156
157 } finally {
158 IOUtils.closeQuietly(input);
159 }
160 }
161
162 public InputStream openStream(URI uri) {
163 try {
164 HttpURLConnection connection = newHttpConnection(uri);
165 return connection.getInputStream();
166
167 } catch (Exception e) {
168 throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
169 }
170 }
171
172 private HttpURLConnection newHttpConnection(URI uri) throws IOException {
173 LoggerFactory.getLogger(getClass()).debug("Download: " + uri + " (" + getProxySynthesis(uri) + ")");
174 HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
175 connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
176 connection.setReadTimeout(TIMEOUT_MILLISECONDS);
177 connection.setUseCaches(true);
178 connection.setInstanceFollowRedirects(true);
179 connection.setRequestProperty("User-Agent", userAgent);
180 return connection;
181 }
182 }
183
184 class ProxyAuthenticator extends Authenticator {
185 private PasswordAuthentication auth;
186
187 ProxyAuthenticator(String user, String password) {
188 auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray());
189 }
190
191 protected PasswordAuthentication getPasswordAuthentication() {
192 return auth;
193 }
194 }