/*******************************************************************************
* Copyright (c) 2011, 2014 Tasktop Technologies and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.commons.repositories.http.core;
import java.io.IOException;
import javax.net.ssl.TrustManager;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.ContentEncodingHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.mylyn.commons.core.net.SslSupport;
import org.eclipse.mylyn.commons.core.net.TrustAllTrustManager;
import org.eclipse.mylyn.commons.core.operations.CancellableOperationMonitorThread;
import org.eclipse.mylyn.commons.core.operations.IOperationMonitor;
import org.eclipse.mylyn.commons.repositories.core.RepositoryLocation;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationCredentials;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationException;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationRequest;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationType;
import org.eclipse.mylyn.commons.repositories.core.auth.CertificateCredentials;
import org.eclipse.mylyn.commons.repositories.core.auth.UserCredentials;
/**
* Provides an abstraction for connecting to a {@link RepositoryLocation} through HTTP.
*
* @author Steffen Pingel
*/
public class CommonHttpClient {
private boolean preemptiveAuthenticationEnabled;
private boolean authenticated;
private final ThreadLocal<BasicHttpContext> context = new ThreadLocal<BasicHttpContext>();
private AuthenticationType<UserCredentials> httpAuthenticationType;
private AbstractHttpClient httpClient;
private final RepositoryLocation location;
private CancellableOperationMonitorThread monitorThread = CancellableOperationMonitorThread.getInstance();
public CommonHttpClient(RepositoryLocation location) {
this.location = location;
this.httpAuthenticationType = AuthenticationType.HTTP;
}
public <T> T executeGet(String requestPath, IOperationMonitor monitor, HttpRequestProcessor<T> processor)
throws IOException {
HttpGet request = new HttpGet(location.getUrl() + requestPath);
DefaultHttpOperation<T> op = new DefaultHttpOperation<T>(this, request, processor);
return op.run(monitor);
}
public HttpResponse execute(HttpRequestBase request, IOperationMonitor monitor) throws IOException {
prepareRequest(request, monitor);
return HttpUtil.execute(getHttpClient(), HttpUtil.createHost(request), getContext(), request, monitor);
}
public HttpContext getContext() {
if (context.get() == null) {
context.set(new BasicHttpContext(null));
}
return context.get();
}
public AuthenticationType<UserCredentials> getHttpAuthenticationType() {
return httpAuthenticationType;
}
public synchronized AbstractHttpClient getHttpClient() {
if (httpClient == null) {
httpClient = createHttpClient(null);
}
return httpClient;
}
public RepositoryLocation getLocation() {
return location;
}
public boolean isAuthenticated() {
return authenticated;
}
public boolean isPreemptiveAuthenticationEnabled() {
return preemptiveAuthenticationEnabled;
}
public boolean needsAuthentication() {
return !isAuthenticated() && getLocation().getCredentials(AuthenticationType.REPOSITORY, false) != null;
}
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
public void setHttpAuthenticationType(AuthenticationType<UserCredentials> httpAuthenticationType) {
this.httpAuthenticationType = httpAuthenticationType;
}
public void setPreemptiveAuthenticationEnabled(boolean preemptiveAuthenticationEnabled) {
this.preemptiveAuthenticationEnabled = preemptiveAuthenticationEnabled;
}
private void prepareRequest(HttpRequestBase request, IOperationMonitor monitor) {
UserCredentials httpCredentials = location.getCredentials(httpAuthenticationType);
if (httpCredentials != null) {
HttpUtil.configureAuthentication(getHttpClient(), location, httpCredentials);
if (isPreemptiveAuthenticationEnabled()) {
// create or pre-populate auth cache
HttpHost host = HttpUtil.createHost(request);
Object authCache = getContext().getAttribute(ClientContext.AUTH_CACHE);
if (authCache == null) {
authCache = new BasicAuthCache();
getContext().setAttribute(ClientContext.AUTH_CACHE, authCache);
}
if (authCache instanceof BasicAuthCache) {
if (((BasicAuthCache) authCache).get(host) == null) {
((BasicAuthCache) authCache).put(host, new BasicScheme());
}
}
}
}
HttpUtil.configureProxy(getHttpClient(), location);
CertificateCredentials socketCredentials = location.getCredentials(AuthenticationType.CERTIFICATE);
if (socketCredentials != null) {
SslSupport support = new SslSupport(new TrustManager[] { new TrustAllTrustManager() },
socketCredentials.getKeyStoreFileName(), socketCredentials.getPassword(),
socketCredentials.getKeyStoreType());
request.getParams().setParameter(SslSupport.class.getName(), support);
} else {
// remove the token that associates certificate credentials with the connection
getContext().removeAttribute(ClientContext.USER_TOKEN);
}
getContext().setAttribute(HttpUtil.CONTEXT_KEY_MONITOR_THREAD, getMonitorThread());
}
protected void authenticate(IOperationMonitor monitor) throws IOException {
}
protected AbstractHttpClient createHttpClient(String userAgent) {
AbstractHttpClient client = new ContentEncodingHttpClient() {
@Override
protected ClientConnectionManager createClientConnectionManager() {
return CommonHttpClient.this.createHttpClientConnectionManager();
}
};
// client.setTargetAuthenticationHandler(new DefaultTargetAuthenticationHandler() {
// @Override
// public boolean isAuthenticationRequested(HttpResponse response, HttpContext context) {
// int statusCode = response.getStatusLine().getStatusCode();
// return statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN;
// }
// });
HttpUtil.configureClient(client, userAgent);
return client;
}
protected ClientConnectionManager createHttpClientConnectionManager() {
return HttpUtil.getConnectionManager();
}
protected <T extends AuthenticationCredentials> T requestCredentials(
AuthenticationRequest<AuthenticationType<T>> request, IProgressMonitor monitor) {
return location.requestCredentials(request, monitor);
}
protected void validate(HttpResponse response, IProgressMonitor monitor) throws AuthenticationException {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
AuthenticationRequest<AuthenticationType<UserCredentials>> request = new AuthenticationRequest<AuthenticationType<UserCredentials>>(
getLocation(), httpAuthenticationType);
throw new AuthenticationException(HttpUtil.getStatusText(statusCode), request);
} else if (statusCode == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
AuthenticationRequest<AuthenticationType<UserCredentials>> request = new AuthenticationRequest<AuthenticationType<UserCredentials>>(
getLocation(), AuthenticationType.PROXY);
throw new AuthenticationException(HttpUtil.getStatusText(statusCode), request);
}
}
public CancellableOperationMonitorThread getMonitorThread() {
return monitorThread;
}
public void setMonitorThread(CancellableOperationMonitorThread monitorThread) {
this.monitorThread = monitorThread;
}
}