/** * Copyright (c) 2009-2016 Freedomotic team http://freedomotic.com * <p> * This file is part of Freedomotic * <p> * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later version. * <p> * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * <p> * You should have received a copy of the GNU General Public License along with * Freedomotic; see the file COPYING. If not, see * <http://www.gnu.org/licenses/>. */ package com.freedomotic.helpers; import com.google.common.collect.ImmutableMap; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.config.RequestConfig; 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.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.apache.commons.io.IOUtils.toByteArray; import org.apache.http.impl.client.HttpClients; /** * @author Enrico Nicoletti */ public class HttpHelper { private static final Logger LOG = LoggerFactory.getLogger(HttpHelper.class.getName()); private static final int DEFAULT_TIMEOUT = 30_000; //30seconds private static final Charset DEFAULT_UTF8 = Charset.forName("UTF-8"); private DocumentBuilder documentBuilder; private XPath xPath; private int connectionTimeout = DEFAULT_TIMEOUT; public HttpHelper() { try { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); documentBuilder = builderFactory.newDocumentBuilder(); } catch (ParserConfigurationException ex) { LOG.error(ex.getMessage()); } XPathFactory xpathFactory = XPathFactory.newInstance(); xPath = xpathFactory.newXPath(); } /** * Get the content of an URL (eg: a webpage) as a string. Content can be * HTML, XML or JSON * * @param url * @return * @throws IOException */ public String retrieveContent(String url) throws IOException { return doGetAsString(url, null, null); } /** * Post the content to given URL and returns result as byte[]. Post content * type should be defined beforehand. * * @param url of the service * @param content in byte format that is going to be post * @param username for basic authentication * @param password for basic authentication * @return post result as byte array * @throws IOException */ public byte[] post(String url, byte[] content, String username, String password) throws IOException { return post(url, content, username, password, ImmutableMap.<String, String>of()); } /** * @param url of the service * @param content in byte format that is going to be post * @param username for basic authentication * @param password for basic authentication * @param headers http headers * @return post result as byte array */ public byte[] post(String url, byte[] content, String username, String password, Map<String, String> headers) throws IOException { return doPost(url, content, username, password, headers); } /** * Get the content of an URL (eg: a webpage) as a string. Content can be * HTML, XML or JSON * * @param url * @param username * @param password * @return * @throws IOException */ public String retrieveContent(String url, String username, String password) throws IOException { return doGetAsString(url, username, password); } /** * Perform an XPath query on the XML content retrieved from the given URL * * @param url The url from wich retrieve the XML content * @param username username if authentication is required. Can be null * @param password password if authentication is required. Can be null * @param xpathQueries any valid xpath query * @return * @throws IOException */ public List<String> queryXml(String url, String username, String password, String... xpathQueries) throws IOException { byte[] xmlContent = doGet(url, username, password); List<String> results = new ArrayList<>(); try { InputSource is = new InputSource(new ByteArrayInputStream(xmlContent)); Document xmlDocument = documentBuilder.parse(is); xmlDocument.getDocumentElement().normalize(); //xpathQuery contains the xpath expression to be applied on the retrieved content for (String xpathQuery : xpathQueries) { String result = xPath.compile(xpathQuery).evaluate(xmlDocument); // Notify an enpy result to the user if (result == null || result.isEmpty()) { LOG.warn("XPath query {} produced no results on content: \n{}", new String[]{xpathQuery, new String(xmlContent)}); result = ""; } results.add(result); } } catch (XPathExpressionException | SAXException | IOException ex) { throw new IOException("Cannot perform the given xpath query", ex); } return results; } /** * Determines the timeout in milliseconds until a connection is established. * A timeout value of zero is interpreted as an infinite timeout. * <br> * See also {@link RequestConfig#getConnectTimeout()} * * @param connectionTimeout timeout in milliseconds */ public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } /** * * * @param url * @param content * @param username * @param password * @param headers * @return * @throws IOException */ private byte[] doPost(String url, byte[] content, String username, String password, Map<String, String> headers) throws IOException { final HttpPost httpPost = new HttpPost(asUri(url)); final HttpEntity httpEntity = new ByteArrayEntity(content); httpPost.setEntity(httpEntity); for (Map.Entry<String, String> header : headers.entrySet()) { httpPost.setHeader(header.getKey(), header.getValue()); } final HttpResponse httpResponse = fireHttpRequest(httpPost, username, password); try (InputStream inputStream = httpResponse.getEntity().getContent()) { return toByteArray(inputStream); } } /** * * * @param url * @param username * @param password * @return * @throws IOException */ private byte[] doGet(String url, String username, String password) throws IOException { HttpResponse httpResponse = fireGetRequest(url, username, password); try (InputStream inputStream = httpResponse.getEntity().getContent()) { return toByteArray(inputStream); } } /** * * * @param url * @param username * @param password * @return * @throws IOException */ private String doGetAsString(String url, String username, String password) throws IOException { HttpResponse httpResponse = fireGetRequest(url, username, password); try (InputStream inputStream = httpResponse.getEntity().getContent()) { byte[] content = toByteArray(inputStream); return new String(content, determineCharsetName(httpResponse)); } } /** * * * @param url * @param username * @param password * @return * @throws IOException */ private HttpResponse fireGetRequest(String url, String username, String password) throws IOException { return fireHttpRequest(new HttpGet(asUri(url)), username, password); } /** * * * @param httpRequest * @param username * @param password * @return * @throws IOException */ private HttpResponse fireHttpRequest(HttpRequestBase httpRequest, String username, String password) throws IOException { HttpClientBuilder builder = HttpClients.custom().useSystemProperties(); RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectionTimeout).build(); builder.setDefaultRequestConfig(requestConfig); if ((username != null) && (password != null)) { final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password)); builder.setDefaultCredentialsProvider(credentialsProvider); } final CloseableHttpClient client = builder.build(); return client.execute(httpRequest); } /** * * * @param response * @return */ private Charset determineCharsetName(HttpResponse response) { ContentType contentType = ContentType.getLenient(response.getEntity()); if (contentType != null && contentType.getCharset() != null) { return contentType.getCharset(); } return DEFAULT_UTF8; } /** * * * @param url * @return * @throws IOException */ private URI asUri(String url) throws IOException { URI uri; try { String decodedUrl = URLDecoder.decode(url, "UTF-8"); uri = new URL(decodedUrl).toURI(); } catch (URISyntaxException | MalformedURLException ex) { throw new IOException("The URL " + url + "' is not properly formatted: " + ex.getMessage(), ex); } return uri; } }