/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.common.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
/**
* A general-purpose, connection-pooling HTTP Client. All methods are
* thread-safe. Provides option for client to handle HTTP redirects
*
* @version $Id$
*/
public class WebClient {
/** Seconds to wait before a connection is established. */
public int TIMEOUT_SECONDS = 20;
/** Seconds to wait while waiting for data over the socket (SO_TIMEOUT). */
public int SOCKET_TIMEOUT_SECONDS = 120;
/** Maxiumum http connections per host */
public int MAX_CONNECTIONS_PER_HOST = 5;
/** Maxiumum total http connections */
public int MAX_TOTAL_CONNECTIONS = 20;
/** Whether to automatically follow HTTP redirects. */
public boolean FOLLOW_REDIRECTS = true;
/**
* Maximum number of redirects to follow per request if FOLLOW_REDIRECTS is
* true.
*/
public int MAX_REDIRECTS = 3;
/**
* What the "User-Agent" request header should say. Default is null, which
* indicates that the header should not be provided.
*/
public String USER_AGENT = null;
private MultiThreadedHttpConnectionManager m_cManager;
/**
* The proxy configuration for the web client.
*/
private static ProxyConfiguration proxy = new ProxyConfiguration();
public WebClient() {
configureConnectionManager();
}
public WebClient(ProxyConfiguration configuration){
proxy = configuration;
configureConnectionManager();
}
private void configureConnectionManager(){
m_cManager = new MultiThreadedHttpConnectionManager();
m_cManager.getParams().setDefaultMaxConnectionsPerHost(MAX_CONNECTIONS_PER_HOST);
m_cManager.getParams().setMaxTotalConnections(MAX_TOTAL_CONNECTIONS);
m_cManager.getParams().setConnectionTimeout(TIMEOUT_SECONDS * 1000);
m_cManager.getParams().setSoTimeout(SOCKET_TIMEOUT_SECONDS * 1000);
}
public HttpClient getHttpClient(String hostOrUrl) throws IOException {
return getHttpClient(hostOrUrl, null);
}
public HttpClient getHttpClient(String hostOrURL,
UsernamePasswordCredentials creds)
throws IOException {
String host = null;
if (hostOrURL != null) {
if (hostOrURL.indexOf("/") != -1) {
URL url = new URL(hostOrURL);
host = url.getHost();
} else {
host = hostOrURL;
}
}
HttpClient client = new HttpClient(m_cManager);
if (host != null && creds != null) {
client.getState().setCredentials(new AuthScope(host,
AuthScope.ANY_PORT),
creds);
client.getParams().setAuthenticationPreemptive(true);
}
if (proxy.isHostProxyable(host)) {
client.getHostConfiguration().setProxy(proxy.getProxyHost(),
proxy.getProxyPort());
if (proxy.hasValidCredentials()) {
client.getState().setProxyCredentials(new AuthScope(proxy.getProxyHost(),
proxy.getProxyPort(),
null),
new UsernamePasswordCredentials(proxy.getProxyUser(),
proxy.getProxyPassword()));
}
}
return client;
}
public HttpInputStream get(String url, boolean failIfNotOK)
throws IOException {
return get(url, failIfNotOK, null);
}
public HttpInputStream get(String url,
boolean failIfNotOK,
String user,
String pass) throws IOException {
UsernamePasswordCredentials creds = null;
if (user != null && !user.equals("") && pass != null && !pass.equals(""))
creds = new UsernamePasswordCredentials(user, pass);
return get(url, failIfNotOK, creds);
}
/**
* Get an HTTP resource with the response as an InputStream, given a URL. If
* FOLLOW_REDIRECTS is true, up to MAX_REDIRECTS redirects will be followed.
* Note that if credentials are provided, for security reasons they will
* only be provided to the FIRST url in a chain of redirects. Note that if
* the HTTP response has no body, the InputStream will be empty. The success
* of a request can be checked with getResponseCode(). Usually you'll want
* to see a 200. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
* for other codes.
*
* @param url
* A URL that we want to do an HTTP GET upon
* @param failIfNotOK
* boolean value indicating if an exception should be thrown if we do
* NOT receive an HTTP 200 response (OK)
* @return HttpInputStream the HTTP response
* @throws IOException
*/
public HttpInputStream get(String url,
boolean failIfNotOK,
UsernamePasswordCredentials creds)
throws IOException {
HttpClient client;
GetMethod getMethod = new GetMethod(url);
if (USER_AGENT != null) {
getMethod.setRequestHeader("User-Agent", USER_AGENT);
}
if (creds != null && creds.getUserName() != null
&& creds.getUserName().length() > 0) {
client = getHttpClient(url, creds);
getMethod.setDoAuthentication(true);
} else {
client = getHttpClient(url);
}
HttpInputStream in = new HttpInputStream(client, getMethod, url);
int status = in.getStatusCode();
if (failIfNotOK) {
if (status != 200) {
//if (followRedirects && in.getStatusCode() == 302){
if (FOLLOW_REDIRECTS && 300 <= status && status <= 399) {
int count = 1;
while (300 <= status && status <= 399
&& count <= MAX_REDIRECTS) {
if (in.getResponseHeader("location") == null) {
throw new IOException("Redirect HTTP response provided no location header.");
}
url = in.getResponseHeader("location").getValue();
in.close();
getMethod = new GetMethod(url);
if (USER_AGENT != null) {
getMethod
.setRequestHeader("User-Agent", USER_AGENT);
}
in = new HttpInputStream(client, getMethod, url);
status = in.getStatusCode();
count++;
}
if (300 <= status && status <= 399) {
in.close();
throw new IOException("Too many redirects");
} else if (status != 200) {
in.close();
throw new IOException("Request failed ["
+ in.getStatusCode() + " " + in.getStatusText()
+ "]");
}
// redirect was successful!
} else {
try {
throw new IOException("Request failed ["
+ in.getStatusCode() + " " + in.getStatusText()
+ "]");
} finally {
try {
in.close();
} catch (Exception e) {
System.err.println("Can't close InputStream: "
+ e.getMessage());
}
}
}
}
}
return in;
}
public String getResponseAsString(String url, boolean failIfNotOK)
throws IOException {
return getResponseAsString(url, failIfNotOK, null);
}
public String getResponseAsString(String url,
boolean failIfNotOK,
UsernamePasswordCredentials creds)
throws IOException {
InputStream in = get(url, failIfNotOK, creds);
// Convert the response into a String.
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(in));
StringBuffer buffer = new StringBuffer();
String line = reader.readLine();
while (line != null) {
buffer.append(line + "\n");
line = reader.readLine();
}
return buffer.toString();
} finally {
try {
in.close();
} catch (Exception e) {
System.err
.println("Can't close InputStream: " + e.getMessage());
}
}
}
}