package com.lmit.jenkins.android.networking; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.CoreProtocolPNames; import android.webkit.WebSettings; import com.lmit.jenkins.android.addon.LocalStorage; import com.lmit.jenkins.android.configuration.Configuration; import com.lmit.jenkins.android.logger.Logger; import com.lmit.jenkinscloud.commons.JenkinsCloudDataNode; import com.lmit.jenkinscloud.commons.JenkinsCloudNode; public abstract class AbstractSecureHttpClient { private Logger log = Logger.getInstance(); protected DefaultHttpClient wrappedDefaultHttpClient; protected String domainName; protected int port; protected String protocol; protected String fullUrl; protected String queryPath; protected HttpCredentials credentials; protected boolean performAuthentication = true; protected HttpUriRequest lastRequest; protected boolean connectionAborted = false; protected String customUserAgentInfo = null; private HttpGet get; private static final Configuration configuration = Configuration.getInstance(); private HttpResponse response; public AbstractSecureHttpClient(DefaultHttpClient httpClient, String url, HttpCredentials credentials, String customUserAgentExtention) { url = makeUrl(url); this.wrappedDefaultHttpClient = httpClient; if (customUserAgentExtention != null) { wrappedDefaultHttpClient.getParams().setParameter( CoreProtocolPNames.USER_AGENT, getDefaultUserAgent(customUserAgentExtention)); } else { wrappedDefaultHttpClient.getParams().setParameter( CoreProtocolPNames.USER_AGENT, getDefaultUserAgent()); } this.credentials = credentials; this.fullUrl = url; this.port = UrlParser.getPort(url); this.protocol = UrlParser.getProtocol(url); this.domainName = UrlParser.getDomainName(url); this.queryPath = UrlParser.getQueryPath(url); if (credentials.getPassword() == null || credentials.getPassword().length() == 0) { performAuthentication = false; } } public static String makeUrl(String url) { if(url.startsWith("http://") || url.startsWith("https://")){ return url; } if (!Configuration.getInstance().getHudsonHostname().endsWith("/")) { url = "/" + url; } url = url.replace(" ", "%20"); return Configuration.getInstance().getHudsonHostname() + url; } public static String getDefaultUserAgent() { return configuration.getUserAgent() + " " + configuration.getProductName() + "/" + configuration.getProductVersion() + " Android/" + Configuration.getDeviceSoftwareVersion(); } public static String getDefaultUserAgent(String customUserAgentExtention) { return configuration.getUserAgent() + " " + Configuration.getInstance().getProductName() + "/" + customUserAgentExtention + "/" + Configuration.getInstance().getProductVersion() + " Android/" + Configuration.getDeviceSoftwareVersion(); } public void abortConnection() { if (lastRequest != null) { connectionAborted = true; lastRequest.abort(); } } public HttpResponse execute(HttpUriRequest request) throws ClientProtocolException, IOException { HttpResponse response = null; addProductSpecHeaders(request); lastRequest = request; log.debug("HTTP-" + request.getMethod() + " " + request.getURI()); response = wrappedDefaultHttpClient.execute(request); log.debug("HTTP-STATUS: " + response.getStatusLine().toString()); return this.response = response; } public HttpResponse executeGetQuery(boolean forceRefresh) throws ClientProtocolException, IOException { return executeGetQuery(forceRefresh, null); } public HttpResponse executeGetQuery(boolean forceRefresh, Map<String, String> headers) throws ClientProtocolException, IOException { get = new HttpGet(fullUrl); setHeaders(forceRefresh, headers, get); return execute(get); } public HttpResponse executePostQuery() throws ClientProtocolException, IOException { return execute(new HttpPost(fullUrl)); } public HttpResponse executePostQuery(Map<String, String> parameters) throws ClientProtocolException, IOException { HttpPost post = new HttpPost(fullUrl); setPostParameters(parameters, post); return execute(post); } public HttpResponse executePostQuery(Map<String, String> parameters, Map<String, String> headers) throws ClientProtocolException, IOException { HttpPost post = new HttpPost(fullUrl); setPostHeaders(headers, post); setPostParameters(parameters, post); return execute(post); } public HttpResponse executePostQuery(byte[] postData, Map<String, String> headers) throws ClientProtocolException, IOException { HttpPost post = new HttpPost(fullUrl); post.setEntity(new ByteArrayEntity(postData)); setPostHeaders(headers, post); return execute(post); } protected void addProductSpecHeaders(HttpUriRequest httpMessage) { httpMessage.addHeader("agentVersion", Configuration.getInstance() .getProductVersion()); httpMessage.addHeader("agentName", Configuration.getInstance() .getProductName()); } public boolean isConnectionAborted() { return connectionAborted; } public void setPostParameters(Map<String, String> parameters, HttpPost post) throws UnsupportedEncodingException { if (parameters != null && parameters.size() > 0) { Iterator<String> keysIter = parameters.keySet().iterator(); List<NameValuePair> nameValuePairList = new LinkedList<NameValuePair>(); while (keysIter.hasNext()) { String key = keysIter.next(); String value = parameters.get(key); nameValuePairList.add(new BasicNameValuePair(key, value)); } post.setEntity(new UrlEncodedFormEntity(nameValuePairList, "UTF-8")); } } public void setPostHeaders(Map<String, String> headers, HttpPost post) throws UnsupportedEncodingException { if (headers != null && headers.size() > 0) { Iterator<String> keysIter = headers.keySet().iterator(); while (keysIter.hasNext()) { String key = keysIter.next(); String value = headers.get(key); post.setHeader(key, value); } } post.setHeader("Content-Type", "application/x-www-form-urlencoded"); } public void setHeaders(boolean forceRefresh, Map<String, String> headers, HttpRequestBase get) throws UnsupportedEncodingException, MalformedURLException { if (headers == null) { headers = Configuration.getInstance().getRequestHeaders(); } if(forceRefresh) { headers.put("Cache-Control", "no-cache"); } else { setETagCacheHeader(get); } if (headers != null && headers.size() > 0) { Iterator<String> keysIter = headers.keySet().iterator(); while (keysIter.hasNext()) { String key = keysIter.next(); String value = headers.get(key); get.setHeader(key, value); } } } public JenkinsCloudNode getCachedNode() { return getCachedNode(this.get.getURI().toString()); } public JenkinsCloudNode getCachedNode(String uri) { JenkinsCloudNode cachedNode = null; String fullBaseUrl = Configuration.getInstance().getHudsonHostname(); String getUrlNormalised; try { getUrlNormalised = getNormalisedURL(makeUrl(uri)); if (getUrlNormalised.startsWith(getNormalisedURL(fullBaseUrl))) { String path = getUrlNormalised.substring(fullBaseUrl.length()); cachedNode = LocalStorage.getInstance().getNode(path); if (cachedNode != null) { cachedNode.setCached(true); } } } catch (MalformedURLException e) { log.error("Invalid request URL " + fullBaseUrl, e); return null; } return cachedNode; } private String getNormalisedURL(String urlString) throws MalformedURLException { URL url = new URL(urlString); String urlPath = url.getPath(); String query = url.getQuery(); while (urlPath.indexOf("//") >= 0) { urlPath = urlPath.replaceAll("//", "/"); } if(query != null && query.indexOf("web=") >= 0) { urlPath += "?" + query; } return url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + urlPath; } private void setETagCacheHeader(HttpRequestBase get) throws MalformedURLException { JenkinsCloudNode cachedNode = getCachedNode(get.getURI().toString()); if (cachedNode != null && cachedNode.getEtag() != null) { get.setHeader("If-None-Match", cachedNode.getEtag()); } } public HttpPost getPost() { return new HttpPost(fullUrl); } public void releaseConnection() { if (response != null && response.getEntity() != null) { try { response.getEntity().consumeContent(); } catch (IOException e) { log.error("Cannot consume response content to release HTTP connection", e); } } } }