io.robe.assets.http.HttpAssetServlet Maven / Gradle / Ivy
package io.robe.assets.http;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.net.HttpHeaders;
import com.google.common.net.MediaType;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* Servlet for serving assets with configuration.
* Cache mechanism holds only paths. Actual byte cache is managed by {@link io.robe.assets.http.HttpAsset}
*
* @see io.dropwizard.servlets.assets.AssetServlet
*/
public class HttpAssetServlet extends HttpServlet {
private Cache cache;
private static final MediaType DEFAULT_MEDIA_TYPE = MediaType.HTML_UTF_8;
private final String resourcePath;
private final String uriPath;
private final String indexFile;
private final Charset defaultCharset;
private final boolean cached;
/**
* Creates a new {@code FileAssetServlet} that serves static assets loaded from {@code resourceURL}
*
* @param resourcePath the base URL from which assets are loaded
* @param uriPath the URI path fragment in which all requests are rooted
* @param indexFile the filename to use when directories are requested, or null to serve no
* indexes
* @param defaultCharset the default character set
*/
public HttpAssetServlet(String resourcePath,
String uriPath,
String indexFile,
Charset defaultCharset,
boolean cached) {
this.resourcePath = fixPath(resourcePath);
uriPath = uriPath.endsWith("/") ? uriPath.substring(0, uriPath.length() - 1) : uriPath;
this.uriPath = uriPath.isEmpty() ? "/" : uriPath;
this.indexFile = indexFile;
this.defaultCharset = defaultCharset;
this.cached = cached;
cache = CacheBuilder.newBuilder().build();
}
/**
* Helps to fix path
*
* @param path to fix
* @return
*/
private String fixPath(String path) {
if (!path.isEmpty()) {
if (!path.endsWith("/"))
return path + '/';
else
return path;
} else
return path;
}
public String getIndexFile() {
return indexFile;
}
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
try {
final StringBuilder builder = new StringBuilder(req.getServletPath());
// If http is empty redirect to index http.
if (req.getPathInfo() != null) {
builder.append(req.getPathInfo());
} else {
builder.insert(0, req.getContextPath());
builder.append("/").append(getIndexFile());
resp.sendRedirect(builder.toString());
return;
}
String assetPath = builder.toString();
//Get from cache if not available load it.
HttpAsset asset = cache.getIfPresent(assetPath);
if (asset == null) {
asset = loadAsset(assetPath);
}
//If still it is null it means nothing to load.
if (asset == null) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Maybe client got it already
if (!isDifferentFromClientCache(req, asset)) {
resp.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
//Modify response identifiers
resp.setDateHeader(HttpHeaders.LAST_MODIFIED, asset.getLastModified());
resp.setHeader(HttpHeaders.ETAG, asset.getETAG());
decideMimeAndEncoding(req, resp);
try (ServletOutputStream output = resp.getOutputStream()) {
output.write(asset.loadAsset());
}
} catch (RuntimeException e) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
private void decideMimeAndEncoding(HttpServletRequest req, HttpServletResponse resp) {
//Decide mimetype
String mime = req.getServletContext().getMimeType(req.getRequestURI());
MediaType mediaType = (mime == null) ? DEFAULT_MEDIA_TYPE : MediaType.parse(mime);
if (defaultCharset != null && mediaType.is(MediaType.ANY_TEXT_TYPE)) {
mediaType = mediaType.withCharset(defaultCharset);
}
resp.setContentType(mediaType.type() + '/' + mediaType.subtype());
if (mediaType.charset().isPresent()) {
resp.setCharacterEncoding(mediaType.charset().get().toString());
}
}
private HttpAsset loadAsset(String path) {
if (!path.startsWith(uriPath))
return null;
String absolutePath = path.substring(uriPath.length());
absolutePath = absolutePath.startsWith("/") ? absolutePath.substring(1) : absolutePath;
absolutePath = absolutePath.endsWith("/") ? absolutePath.substring(0, absolutePath.length() - 1) : absolutePath;
absolutePath = this.resourcePath + absolutePath;
HttpAsset asset = new HttpAsset(absolutePath, cached);
cache.put(path, asset);
return asset;
}
private long msToSec(long time) {
return time / 1000;
}
private boolean isDifferentFromClientCache(HttpServletRequest req, HttpAsset asset) {
return asset.getETAG().equals(req.getHeader(HttpHeaders.IF_NONE_MATCH)) ||
(msToSec(req.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE)) >= msToSec(asset.getLastModified()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy