com.vaadin.server.communication.PublishedFileHandler Maven / Gradle / Ivy
/*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.server.communication;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletResponse;
import com.vaadin.annotations.JavaScript;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.server.Constants;
import com.vaadin.server.LegacyCommunicationManager;
import com.vaadin.server.RequestHandler;
import com.vaadin.server.ServletPortletHelper;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.ApplicationConstants;
/**
* Serves a connector resource from the classpath if the resource has previously
* been registered by calling
* {@link LegacyCommunicationManager#registerDependency(String, Class)}. Sending
* arbitrary files from the classpath is prevented by only accepting resource
* names that have explicitly been registered. Resources can currently only be
* registered by including a {@link JavaScript} or {@link StyleSheet} annotation
* on a Connector class.
*
* @author Vaadin Ltd
* @since 7.1
*/
public class PublishedFileHandler implements RequestHandler {
/**
* Writes the connector resource identified by the request URI to the
* response. If a published resource corresponding to the URI path is not
* found, writes a HTTP Not Found error to the response.
*/
@Override
public boolean handleRequest(VaadinSession session, VaadinRequest request,
VaadinResponse response) throws IOException {
if (!ServletPortletHelper.isPublishedFileRequest(request)) {
return false;
}
String pathInfo = request.getPathInfo();
// + 2 to also remove beginning and ending slashes
String fileName = pathInfo.substring(
ApplicationConstants.PUBLISHED_FILE_PATH.length() + 2);
final String mimetype = response.getService().getMimeType(fileName);
// Security check: avoid accidentally serving from the UI of the
// classpath instead of relative to the context class
if (fileName.startsWith("/")) {
getLogger()
.warning("Published file request starting with / rejected: "
+ fileName);
response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName);
return true;
}
// Check that the resource name has been registered
session.lock();
Class> context;
try {
context = session.getCommunicationManager().getDependencies()
.get(fileName);
} finally {
session.unlock();
}
// Security check: don't serve resource if the name hasn't been
// registered in the map
if (context == null) {
getLogger().warning(
"Rejecting published file request for file that has not been published: "
+ fileName);
response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName);
return true;
}
// Resolve file relative to the location of the context class
InputStream in = context.getResourceAsStream(fileName);
if (in == null) {
getLogger().warning(fileName + " published by " + context.getName()
+ " not found. Verify that the file "
+ context.getPackage().getName().replace('.', '/') + '/'
+ fileName + " is available on the classpath.");
response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName);
return true;
}
// Set caching for the published file
String cacheControl = "public, max-age=0, must-revalidate";
int resourceCacheTime = request.getService()
.getDeploymentConfiguration().getResourceCacheTime();
if (resourceCacheTime > 0) {
cacheControl = "max-age=" + String.valueOf(resourceCacheTime);
}
response.setHeader("Cache-Control", cacheControl);
OutputStream out = null;
try {
if (mimetype != null) {
response.setContentType(mimetype);
}
out = response.getOutputStream();
final byte[] buffer = new byte[Constants.DEFAULT_BUFFER_SIZE];
int bytesRead = 0;
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
try {
in.close();
} catch (Exception e) {
// Do nothing
}
if (out != null) {
try {
out.close();
} catch (Exception e) {
// Do nothing
}
}
}
return true;
}
private static final Logger getLogger() {
return Logger.getLogger(PublishedFileHandler.class.getName());
}
}