/*
* Copyright 2001-2013 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package com.uwyn.rife.tools;
import javax.net.ssl.*;
import javax.servlet.http.Cookie;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
public abstract class HttpUtils
{
public static final String CHARSET = "charset=";
public static final Pattern CHARSET_PATTERN = Pattern.compile(";\\s*" + CHARSET + "(.*)$");
private static final String METHOD_GET = "GET";
private static final String METHOD_POST = "POST";
private static final String HEADER_COOKIE = "Cookie";
private static final String HEADER_CONTENTTYPE = "Content-Type";
private static final String HEADER_CONTENTLENGTH = "Content-Length";
private static final String HEADER_SETCOOKIE = "Set-Cookie";
private static final String CONTENTTYPE_FORM_URLENCODED = "application/x-www-form-urlencoded";
/**
* Extracts only the mime-type from a Content-Type HTTP header. Thus a header
* like this: <code>text/html;charset=UTF-8</code> will return: <code>text/html</code>
*
* @param contentType the Content-Type header
* @return the content type header without the charset
* @since 1.6
*/
public static String extractMimeTypeFromContentType(String contentType)
{
int charset_index = contentType.indexOf(CHARSET);
if (charset_index != -1)
{
char indexed_char;
do
{
indexed_char = contentType.charAt(--charset_index);
}
while (' ' == indexed_char);
if (';' == indexed_char)
{
return contentType.substring(0, charset_index);
}
}
return contentType;
}
public static String getConnectionContent(HttpURLConnection connection)
throws IOException
{
// content character set
String charset = StringUtils.ENCODING_ISO_8859_1;
String content_type = connection.getContentType();
if (content_type != null)
{
Matcher content_type_matcher = CHARSET_PATTERN.matcher(content_type);
if (content_type_matcher.find())
{
charset = content_type_matcher.group(1);
}
}
ByteArrayOutputStream byte_output = new ByteArrayOutputStream();
if (!getConnectionContent(connection, byte_output))
{
return null;
}
byte[] bytes = byte_output.toByteArray();
return new String(bytes, charset);
}
public static boolean getConnectionContent(HttpURLConnection connection, OutputStream output)
throws IOException
{
InputStream input;
try
{
input = connection.getInputStream();
}
catch (FileNotFoundException e)
{
return false;
}
// headers
Map header_fields = connection.getHeaderFields();
// content encoding
List content_encoding = (List)header_fields.get("Content-Encoding");
if (content_encoding != null &&
content_encoding.contains("gzip"))
{
input = new GZIPInputStream(input);
}
BufferedInputStream buffered_input = new BufferedInputStream(input);
byte[] buffer = new byte[8 * 1024];
int count = 0;
do
{
output.write(buffer, 0, count);
count = buffered_input.read(buffer, 0, buffer.length);
}
while (-1 != count);
return true;
}
public static Page retrievePage(String absoluteUrl)
throws IOException
{
return retrievePage(absoluteUrl, true);
}
public static Page retrievePage(String absoluteUrl, boolean downloadContent)
throws IOException
{
return new Request(absoluteUrl).retrieve(downloadContent);
}
public static class Request
{
private String url = null;
private String method = null;
private Map<String, String> header = null;
private StringBuilder queryParams = null;
private StringBuilder postParams = null;
public Request(String absoluteUrl)
{
if (null == absoluteUrl)
{
absoluteUrl = "";
}
url = absoluteUrl;
}
public Request method(String method)
{
this.method = method;
return this;
}
public Request queryParams(String[][] queryParams)
{
if (null != queryParams)
{
if (null == this.queryParams)
{
this.queryParams = new StringBuilder();
}
params(this.queryParams, queryParams);
}
return this;
}
public Request queryParams(Map<String, String[]> queryParams)
{
if (null != queryParams)
{
if (null == this.queryParams)
{
this.queryParams = new StringBuilder();
}
params(this.queryParams, queryParams);
}
return this;
}
public Request queryParam(String key, String value)
{
if (null == queryParams)
{
queryParams = new StringBuilder();
}
param(queryParams, key, value);
return this;
}
public Request postParams(String[][] postParams)
{
if (null != postParams)
{
if (null == this.postParams)
{
this.postParams = new StringBuilder();
}
params(this.postParams, postParams);
}
return this;
}
public Request postParams(Map<String, String[]> postParams)
{
if (null != postParams)
{
if (null == this.postParams)
{
this.postParams = new StringBuilder();
}
params(this.postParams, postParams);
}
return this;
}
public Request postParam(String key, String value)
{
if (null == postParams)
{
postParams = new StringBuilder();
}
param(postParams, key, value);
return this;
}
private void param(StringBuilder store, String key, String value)
{
if (null != key &&
null != value)
{
if (store.length() > 0)
{
store.append("&");
}
store.append(StringUtils.encodeUrl(key));
store.append("=");
store.append(StringUtils.encodeUrl(value));
}
}
private void params(StringBuilder store, String[][] params)
{
for (String[] param : params)
{
for (int i = 1; i < param.length; i++)
{
param(store, param[0], param[i]);
}
}
}
private void params(StringBuilder store, Map<String, String[]> params)
{
for (Map.Entry<String, String[]> param_entry : params.entrySet())
{
for (String value : param_entry.getValue())
{
param(store, param_entry.getKey(), value);
}
}
}
public Request headers(String[][] headers)
{
if (null != headers)
{
if (null == header)
{
header = new LinkedHashMap<>();
}
LinkedHashMap<String, List<String>> header_list_map = new LinkedHashMap<>();
for (String[] header : headers)
{
if (null != header[0] &&
null != header[1])
{
List<String> header_values = header_list_map.get(header[0]);
if (null == header_values)
{
header_values = new ArrayList<>();
header_list_map.put(header[0], header_values);
}
boolean first = true;
for (String header_value : header)
{
if (first)
{
first = false;
continue;
}
header_values.add(header_value);
}
}
}
List<String> headers_values;
for (String headers_name : header_list_map.keySet())
{
headers_values = header_list_map.get(headers_name);
header.put(headers_name, StringUtils.join(headers_values, ","));
}
}
return this;
}
public Request headers(Map<String, String> headers)
{
if (null != headers)
{
if (null == header)
{
header = new LinkedHashMap<>();
}
header.putAll(headers);
}
return this;
}
public Request header(String name, String content)
{
if (null != name &&
null != content)
{
if (null == header)
{
header = new LinkedHashMap<>();
}
header.put(name, content);
}
return this;
}
public Request cookie(String name, String value)
{
String current_cookie_header = null;
if (header != null)
{
current_cookie_header = header.get(HEADER_COOKIE);
}
if (null == current_cookie_header)
{
current_cookie_header = "$Version=\"1\"";
}
StringBuilder cookie_header = new StringBuilder(current_cookie_header);
cookie_header.append("; ");
cookie_header.append(name);
cookie_header.append("=\"");
cookie_header.append(value);
cookie_header.append("\"");
header(HEADER_COOKIE, cookie_header.toString());
return this;
}
public Page retrieve()
throws IOException
{
return retrieve(true);
}
public Page retrieve(boolean downloadContent)
throws IOException
{
return retrieve(downloadContent, null);
}
public Page retrieve(boolean downloadContent, OutputStream output)
throws IOException
{
Page page = null;
URL url;
HttpURLConnection connection = null;
try
{
// add the query parameters to the URL if thzy're present
if (queryParams != null &&
queryParams.length() > 0)
{
StringBuilder buffer = new StringBuilder(this.url);
if (!this.url.contains("?"))
{
buffer.append("?");
}
else
{
buffer.append("&");
}
buffer.append(queryParams.toString());
this.url = buffer.toString();
}
// create a new URL
url = new URL(this.url);
// setup the request method
if (null == method)
{
if (postParams != null)
{
method = METHOD_POST;
// put the correct content type for a post request
if (null == header ||
!header.containsKey(HEADER_CONTENTTYPE))
{
header(HEADER_CONTENTTYPE, CONTENTTYPE_FORM_URLENCODED);
}
}
else
{
method = METHOD_GET;
}
}
if (postParams != null)
{
// put the correct content length for post requests
header(HEADER_CONTENTLENGTH, String.valueOf(postParams.length()));
}
// make untrusted SSL certificates work
if (url.getProtocol().equals("https"))
{
try
{
java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
X509TrustManager tm = new MyX509TrustManager();
HostnameVerifier hm = new MyHostnameVerifier();
KeyManager[] km = null;
TrustManager[] tma = {tm};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(km, tma, new java.security.SecureRandom());
SSLSocketFactory sf1 = sc.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sf1);
HttpsURLConnection.setDefaultHostnameVerifier(hm);
}
catch (KeyManagementException | NoSuchAlgorithmException e)
{
IOException e2 = new IOException();
e2.initCause(e);
throw e2;
}
}
// setup the connection
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod(method);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(true);
// put the headers as properties to the connection
if (header != null)
{
for (String headers_name : header.keySet())
{
connection.setRequestProperty(headers_name, header.get(headers_name));
}
}
// send the form parameters
if (postParams != null)
{
try (DataOutputStream out = new DataOutputStream(connection.getOutputStream()))
{
out.writeBytes(postParams.toString());
out.flush();
}
}
// download the connection content if that's needed
String connection_content = null;
if (downloadContent)
{
if (output != null)
{
getConnectionContent(connection, output);
}
else
{
connection_content = getConnectionContent(connection);
}
}
// construct a new result page from the connection result
page = new Page(connection_content,
connection.getHeaderFields(),
connection.getResponseCode(),
connection.getResponseMessage());
connection.disconnect();
}
finally
{
if (null != connection)
{
connection.disconnect();
}
}
return page;
}
}
static class MyHostnameVerifier implements HostnameVerifier
{
public boolean verify(String urlHostname, SSLSession session)
{
return true;
}
}
static class MyX509TrustManager implements X509TrustManager
{
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
}
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}
public static class Page
{
private String mContent = null;
private int mResponseCode = -1;
private String mResponseMessage = null;
private Map<String, List<String>> mHeaders = null;
Page(String content, Map<String, List<String>> headers, int responseCode, String responseMessage)
{
mContent = content;
mHeaders = new HashMap<>(headers);
mResponseCode = responseCode;
mResponseMessage = responseMessage;
}
public String getContent()
{
return mContent;
}
public Map<String, List<String>> getHeaders()
{
return mHeaders;
}
public List<String> getHeader(String name)
{
return mHeaders.get(name);
}
public String getHeaderField(String name)
{
List<String> header = getHeader(name);
if (null == header)
{
return null;
}
return header.get(0);
}
public int getResponseCode()
{
return mResponseCode;
}
public String getResponseMessage()
{
return mResponseMessage;
}
public String getContentType()
{
return getHeaderField(HEADER_CONTENTTYPE);
}
public boolean checkReceivedCookies(Cookie[] cookies)
{
if (!getHeaders().containsKey(HEADER_SETCOOKIE))
{
return false;
}
HashSet<Cookie> matched_cookies = new HashSet<>();
List<String> received_cookies;
received_cookies = getHeaders().get(HEADER_SETCOOKIE);
for (Cookie cookie : cookies)
{
boolean found = false;
for (String header : received_cookies)
{
if (header.startsWith(cookie.getName() + "=" + cookie.getValue()))
{
found = true;
break;
}
}
if (!found)
{
return false;
}
if (matched_cookies.contains(cookie))
{
return false;
}
matched_cookies.add(cookie);
}
return true;
}
}
}