package com.flicklib.service; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLEncoder; import java.security.AccessControlException; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.flicklib.tools.IOTools; import com.google.inject.name.Named; public class UrlConnectionResolver implements SourceLoader { private static final Logger LOGGER = LoggerFactory.getLogger(UrlConnectionResolver.class); static { try { CookieManager manager = new CookieManager(null, new CookiePolicy() { @Override public boolean shouldAccept(URI uri, HttpCookie cookie) { if (cookie.getMaxAge() == 0) { cookie.setMaxAge(-1); } if (cookie.getDomain() == null) { cookie.setDomain(uri.getHost()); } return true; } }); try { CookieHandler.setDefault(manager); } catch (AccessControlException ace) { LOGGER.warn("cookie handler initialization failed!"); } } catch (java.lang.NoClassDefFoundError ncde) { LOGGER.warn("CookieManager is not accessible " + ncde.getMessage(), ncde); } } // TODO detect encoding? private static final String ENCODING = "UTF-8"; private final boolean hideAgent = true; private Integer timeOut = null; private final boolean internalRedirects = true; public UrlConnectionResolver(@Named(value = Constants.HTTP_TIMEOUT) final Integer timeout) { this.timeOut = timeout; } @Override public Source loadSource(String url) throws IOException { URL httpUrl = new URL(url); HttpURLConnection connection = (HttpURLConnection) httpUrl.openConnection(); setupConnection(connection); return processRequest(url, connection); } @Override public Source loadSource(String url, boolean useCache) throws IOException { return loadSource(url); } private Source processRequest(String reqUrl, HttpURLConnection connection) throws IOException, UnsupportedEncodingException { InputStream input = null; Source source = null; Reader reader = null; try { input = connection.getInputStream(); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_PERM) { String newLocation = connection.getHeaderField("Location"); if (newLocation != null) { URL redirectUrl = new URL(connection.getURL(), newLocation); LOGGER.info("redirect to " + redirectUrl.toString()); return loadSource(redirectUrl.toExternalForm()); } } else { String encoding = connection.getContentEncoding(); String contentType = connection.getHeaderField("Content-Type"); if (encoding == null) { if (contentType != null && contentType.indexOf("charset") != -1) { encoding = contentType.replaceAll(".*charset=([\\w-]*)(;*)", "$1"); } } if (encoding == null) { // the old default ... encoding = "ISO-8859-1"; } reader = new InputStreamReader(input, encoding.toUpperCase()); String content = IOTools.readerToString(reader); source = new Source(connection.getURL().toString(), content, contentType, reqUrl); LOGGER.info("request for " + connection.getURL().toString() + " processed, result content type : " + contentType + ", encoding :" + encoding + ", size:" + source.getContent().length()); } } finally { IOTools.close(reader); IOTools.close(input); } return source; } private void setupConnection(final HttpURLConnection connection) { if (hideAgent) { connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5"); } connection.setInstanceFollowRedirects(internalRedirects); if (timeOut != null) { connection.setReadTimeout(timeOut); } } /** {@inheritDoc} */ @Override public Source post(String url, Map<String, String> parameters, Map<String, String> headers) throws IOException { URL httpUrl = new URL(url); HttpURLConnection connection = (HttpURLConnection) httpUrl.openConnection(); setupConnection(connection); connection.setRequestMethod("POST"); connection.setDoOutput(true); for (Entry<String, String> entry : headers.entrySet()) { connection.setRequestProperty(entry.getKey(), entry.getValue()); } StringBuilder buf = new StringBuilder(); boolean first = true; for (Entry<String, String> entry : parameters.entrySet()) { if (first) { first = false; } else { buf.append('&'); } buf.append(URLEncoder.encode(entry.getKey(), ENCODING)).append('=').append(URLEncoder.encode(entry.getValue(), ENCODING)); } OutputStream outputStream = null; try { outputStream = connection.getOutputStream(); outputStream.write(buf.toString().getBytes(ENCODING)); } finally { IOTools.close(outputStream); } return processRequest(url, connection); } @Override public RestBuilder url(String url) { throw new UnsupportedOperationException("not implemented"); } }