de.mklinger.qetcher.liferay.client.impl.htmlinliner.ResourceLoaderImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qetcher-adapter-liferay-71 Show documentation
Show all versions of qetcher-adapter-liferay-71 Show documentation
Qetcher Liferay 7.1.x Adapter
The newest version!
package de.mklinger.qetcher.liferay.client.impl.htmlinliner;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.Optional;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.WriterOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.mklinger.qetcher.client.QetcherClientException;
import de.mklinger.qetcher.client.model.v1.MediaType;
import de.mklinger.qetcher.liferay.client.impl.abstraction.liferay71.LiferayAbstractionFactory;
/**
* @author Marc Klinger - mklinger[at]mklinger[dot]de
*/
public class ResourceLoaderImpl implements ResourceLoader {
private static final Logger LOG = LoggerFactory.getLogger(ResourceLoaderImpl.class);
private static final Charset UTF_8 = Charset.forName("UTF-8");
@Override
public Optional getContents(final URI url, final URI referrer) {
LOG.info("Fetching {} for inline contents", url);
try {
final URLConnection connection = get(url, referrer);
final Charset charset = getCharset(connection);
final StringWriter sw = new StringWriter();
final WriterOutputStream wout = new WriterOutputStream(sw, charset);
try (InputStream in = connection.getInputStream()) {
IOUtils.copy(in, wout);
}
wout.flush();
final String contents = sw.toString();
return Optional.of(contents);
} catch (final Exception e) {
// no stack trace here.
LOG.info("Error getting contents for inlining: {}", url);
LOG.debug("Error getting contents for inlining had exception:", e);
return Optional.empty();
}
}
@Override
public Optional getExternalInlineImgSrc(final URI url, final URI referrer) {
LOG.info("Fetching {} for inline image", url);
try {
final URLConnection connection = get(url, referrer);
final String contentType = getImageContentType(connection);
try (InputStream in = connection.getInputStream()) {
return Optional.of(getInlineImgSrc(contentType, in));
}
} catch (final Exception e) {
// no stack trace here.
LOG.info("Error getting contents for inlining: {}", url);
LOG.debug("Error getting contents for inlining had exception:", e);
return Optional.empty();
}
}
@Override
public String getInlineImgSrc(final String contentType, final InputStream in) throws IOException {
final byte[] data = IOUtils.toByteArray(in);
final String base64Data = Base64.getEncoder().encodeToString(data);
final StringBuilder sb = new StringBuilder(base64Data.length() + "data:".length() + contentType.length() + ";base64,".length());
sb.append("data:");
sb.append(contentType);
sb.append(";base64,");
sb.append(base64Data);
return sb.toString();
}
private URLConnection get(final URI uri, final URI referrer) throws IOException {
final URLConnection connection = uri.toURL().openConnection();
if (isSameHost(uri, referrer)) {
LOG.debug("Using cookies for url {}", uri);
setCookies(connection);
}
connection.connect();
if (connection instanceof HttpURLConnection) {
final int statusCode = ((HttpURLConnection)connection).getResponseCode();
if (statusCode != 200) {
throw new QetcherClientException("Non 200 status code (" + statusCode + ") for content: " + uri);
}
}
return connection;
}
private boolean isSameHost(final URI uri1, final URI uri2) {
return
uri1.getScheme() != null && uri1.getScheme().equals(uri2.getScheme()) &&
uri1.getHost() != null && uri1.getHost().equals(uri2.getHost()) &&
uri1.getPort() == uri2.getPort();
}
private void setCookies(final URLConnection connection) {
final HttpServletRequest request = LiferayAbstractionFactory.getInstance().getPortalTool().getHttpServletRequest();
if (request == null) {
LOG.debug("No request available");
return;
}
final Cookie[] cookies = request.getCookies();
if (cookies == null || cookies.length == 0) {
LOG.debug("No cookies");
return;
}
for (final Cookie cookie : cookies) {
// XXX escape cookie name/value ??
// Cookie names and values seem not to be escapable. Only certain
// characters are allowed for name and for value.
// For value see https://tools.ietf.org/html/rfc6265.html.
// Should we validate here and ignore invalid cookies?
connection.addRequestProperty("Cookie", cookie.getName() + "=" + cookie.getValue());
}
}
private Charset getCharset(final URLConnection connection) {
final String contentType = connection.getContentType();
if (contentType != null && !contentType.isEmpty()) {
final MediaType mediaType = MediaType.valueOf(contentType);
final Optional responseCharset = mediaType.getParameter("charset");
if (responseCharset.isPresent()) {
try {
final Charset charset = Charset.forName(responseCharset.get());
LOG.debug("Using response charset: {}", charset);
return charset;
} catch (final Exception e) {
// ignore
}
}
}
return UTF_8;
}
private String getImageContentType(final URLConnection connection) {
final String contentType = connection.getContentType();
if (contentType == null) {
throw new QetcherClientException("No content type available for inline image");
}
final MediaType mediaType = MediaType.valueOf(contentType);
if (!"image".equals(mediaType.getType())) {
throw new QetcherClientException("Unsupported content type for inline image: '" + contentType + "'");
}
return mediaType.getFullType();
}
@Override
public void close() {
// Do nothing
}
}