001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
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 */
020package org.sonar.api.utils;
021
022import com.google.common.base.Joiner;
023import com.google.common.collect.Lists;
024import org.apache.commons.io.FileUtils;
025import org.apache.commons.io.IOUtils;
026import org.slf4j.LoggerFactory;
027import org.sonar.api.BatchComponent;
028import org.sonar.api.ServerComponent;
029import org.sonar.api.config.Settings;
030import org.sonar.api.platform.Server;
031
032import java.io.File;
033import java.io.FileOutputStream;
034import java.io.IOException;
035import java.io.InputStream;
036import java.net.*;
037import java.util.List;
038
039/**
040 * This component downloads HTTP files
041 *
042 * @since 2.2
043 */
044public class HttpDownloader implements BatchComponent, ServerComponent {
045
046  public static final int TIMEOUT_MILLISECONDS = 20 * 1000;
047
048  private String userAgent;
049
050  public HttpDownloader(Server server, Settings settings) {
051    this(settings, server.getVersion());
052  }
053
054  public HttpDownloader(Settings settings) {
055    this(settings, null);
056  }
057
058  private HttpDownloader(Settings settings, String userAgent) {
059    initProxy(settings);
060    initUserAgent(userAgent);
061  }
062
063  private void initProxy(Settings settings) {
064    propagateProxySystemProperties(settings);
065    if (requiresProxyAuthentication(settings)) {
066      registerProxyCredentials(settings);
067    }
068  }
069
070  private void initUserAgent(String sonarVersion) {
071    userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion));
072    System.setProperty("http.agent", userAgent);
073  }
074
075  public String getProxySynthesis(URI uri) {
076    return getProxySynthesis(uri, ProxySelector.getDefault());
077  }
078
079  static String getProxySynthesis(URI uri, ProxySelector proxySelector) {
080    List<String> descriptions = Lists.newArrayList();
081    List<Proxy> proxies = proxySelector.select(uri);
082    if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) {
083      descriptions.add("no proxy");
084    } else {
085      for (Proxy proxy : proxies) {
086        if (!proxy.type().equals(Proxy.Type.DIRECT)) {
087          descriptions.add("proxy: " + proxy.address().toString());
088        }
089      }
090    }
091    return Joiner.on(", ").join(descriptions);
092  }
093
094  private void registerProxyCredentials(Settings settings) {
095    Authenticator.setDefault(new ProxyAuthenticator(settings.getString("http.proxyUser"), settings
096        .getString("http.proxyPassword")));
097  }
098
099  private boolean requiresProxyAuthentication(Settings settings) {
100    return settings.getString("http.proxyUser") != null;
101  }
102
103  private void propagateProxySystemProperties(Settings settings) {
104    propagateSystemProperty(settings, "http.proxyHost");
105    propagateSystemProperty(settings, "http.proxyPort");
106    propagateSystemProperty(settings, "http.nonProxyHosts");
107    propagateSystemProperty(settings, "http.auth.ntlm.domain");
108    propagateSystemProperty(settings, "socksProxyHost");
109    propagateSystemProperty(settings, "socksProxyPort");
110  }
111
112  private void propagateSystemProperty(Settings settings, String key) {
113    if (settings.getString(key) != null) {
114      System.setProperty(key, settings.getString(key));
115    }
116  }
117
118  public void download(URI uri, File toFile) {
119    InputStream input = null;
120    FileOutputStream output = null;
121    try {
122      HttpURLConnection connection = newHttpConnection(uri);
123      output = new FileOutputStream(toFile, false);
124      input = connection.getInputStream();
125      IOUtils.copy(input, output);
126
127    } catch (Exception e) {
128      IOUtils.closeQuietly(output);
129      FileUtils.deleteQuietly(toFile);
130      throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
131
132    } finally {
133      IOUtils.closeQuietly(input);
134      IOUtils.closeQuietly(output);
135    }
136  }
137
138  public byte[] download(URI uri) {
139    InputStream input = null;
140    try {
141      HttpURLConnection connection = newHttpConnection(uri);
142      input = connection.getInputStream();
143      return IOUtils.toByteArray(input);
144
145    } catch (Exception e) {
146      throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
147
148    } finally {
149      IOUtils.closeQuietly(input);
150    }
151  }
152
153  public String downloadPlainText(URI uri, String encoding) {
154    InputStream input = null;
155    try {
156      HttpURLConnection connection = newHttpConnection(uri);
157      input = connection.getInputStream();
158      return IOUtils.toString(input, encoding);
159
160    } catch (Exception e) {
161      throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
162
163    } finally {
164      IOUtils.closeQuietly(input);
165    }
166  }
167
168  public InputStream openStream(URI uri) {
169    try {
170      HttpURLConnection connection = newHttpConnection(uri);
171      return connection.getInputStream();
172
173    } catch (Exception e) {
174      throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
175    }
176  }
177
178  private HttpURLConnection newHttpConnection(URI uri) throws IOException {
179    LoggerFactory.getLogger(getClass()).debug("Download: " + uri + " (" + getProxySynthesis(uri) + ")");
180    HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
181    connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
182    connection.setReadTimeout(TIMEOUT_MILLISECONDS);
183    connection.setUseCaches(true);
184    connection.setInstanceFollowRedirects(true);
185    connection.setRequestProperty("User-Agent", userAgent);
186    return connection;
187  }
188}
189
190class ProxyAuthenticator extends Authenticator {
191  private PasswordAuthentication auth;
192
193  ProxyAuthenticator(String user, String password) {
194    auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray());
195  }
196
197  @Override
198  protected PasswordAuthentication getPasswordAuthentication() {
199    return auth;
200  }
201}