All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.tools.development.jetty9.LocalResourceFileServlet Maven / Gradle / Ivy

/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.appengine.tools.development.jetty9;

import com.google.apphosting.utils.config.AppEngineWebXml;
import com.google.apphosting.utils.config.WebXml;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;

/**
 * {@code ResourceFileServlet} is a copy of {@code
 * org.mortbay.jetty.servlet.DefaultServlet} that has been trimmed
 * down to only support the subset of features that we want to take
 * advantage of (e.g. no gzipping, no chunked encoding, no buffering,
 * etc.).  A number of Jetty-specific optimizations and assumptions
 * have also been removed (e.g. use of custom header manipulation
 * API's, use of {@code ByteArrayBuffer} instead of Strings, etc.).
 *
 * 

A few remaining Jetty-centric details remain, such as use of the * {@link ContextHandler.Context} class, and Jetty-specific request * attributes, but these are specific cases where there is no * servlet-engine-neutral API available. This class also uses Jetty's * {@link Resource} class as a convenience, but could be converted to * use {@link javax.servlet.ServletContext#getResource(String)} instead. * */ public class LocalResourceFileServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(LocalResourceFileServlet.class.getName()); private StaticFileUtils staticFileUtils; private Resource resourceBase; private String[] welcomeFiles; private String resourceRoot; /** * Initialize the servlet by extracting some useful configuration * data from the current {@link javax.servlet.ServletContext}. */ @Override public void init() throws ServletException { ContextHandler.Context context = (ContextHandler.Context) getServletContext(); staticFileUtils = new StaticFileUtils(context); // AFAICT, there is no real API to retrieve this information, so // we access Jetty's internal state. welcomeFiles = context.getContextHandler().getWelcomeFiles(); AppEngineWebXml appEngineWebXml = (AppEngineWebXml) getServletContext().getAttribute( "com.google.appengine.tools.development.appEngineWebXml"); resourceRoot = appEngineWebXml.getPublicRoot(); try { String base; if (resourceRoot.startsWith(URIUtil.SLASH)) { base = resourceRoot; } else { base = URIUtil.SLASH + resourceRoot; } // In Jetty 9 "//public" is not seen as "/public" . resourceBase = Resource.newResource(context.getResource(base)); } catch (MalformedURLException ex) { logger.log(Level.WARNING, "Could not initialize:", ex); throw new ServletException(ex); } } public static final java.lang.String __INCLUDE_JETTY = "javax.servlet.include.request_uri"; public static final java.lang.String __INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path"; public static final java.lang.String __INCLUDE_PATH_INFO = "javax.servlet.include.path_info"; public static final java.lang.String __FORWARD_JETTY = "javax.servlet.forward.request_uri"; /** * Retrieve the static resource file indicated. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String servletPath; String pathInfo; AppEngineWebXml appEngineWebXml = (AppEngineWebXml) getServletContext().getAttribute( "com.google.appengine.tools.development.appEngineWebXml"); WebXml webXml = (WebXml) getServletContext().getAttribute( "com.google.appengine.tools.development.webXml"); Boolean forwarded = request.getAttribute(__FORWARD_JETTY) != null; if (forwarded == null) { forwarded = Boolean.FALSE; } Boolean included = request.getAttribute(__INCLUDE_JETTY) != null; if (included != null && included) { servletPath = (String) request.getAttribute(__INCLUDE_SERVLET_PATH); pathInfo = (String) request.getAttribute(__INCLUDE_PATH_INFO); if (servletPath == null) { servletPath = request.getServletPath(); pathInfo = request.getPathInfo(); } } else { included = Boolean.FALSE; servletPath = request.getServletPath(); pathInfo = request.getPathInfo(); } String pathInContext = URIUtil.addPaths(servletPath, pathInfo); if (maybeServeWelcomeFile(pathInContext, included, request, response)) { // We served a welcome file (either via redirecting, forwarding, or including). return; } // Find the resource Resource resource = null; try { resource = getResource(pathInContext); // Handle resource if (resource != null && resource.isDirectory()) { if (included || staticFileUtils.passConditionalHeaders(request, response, resource)) { response.sendError(HttpServletResponse.SC_FORBIDDEN); } } else { if (resource == null || !resource.exists()) { logger.warning("No file found for: " + pathInContext); response.sendError(HttpServletResponse.SC_NOT_FOUND); } else { boolean isStatic = appEngineWebXml.includesStatic(resourceRoot + pathInContext); boolean isResource = appEngineWebXml.includesResource( resourceRoot + pathInContext); boolean usesRuntime = webXml.matches(pathInContext); Boolean isWelcomeFile = (Boolean) request.getAttribute("com.google.appengine.tools.development.isWelcomeFile"); if (isWelcomeFile == null) { isWelcomeFile = false; } if (!isStatic && !usesRuntime && !(included || forwarded)) { logger.warning( "Can not serve " + pathInContext + " directly. " + "You need to include it in in your " + "appengine-web.xml."); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } else if (!isResource && !isWelcomeFile && (included || forwarded)) { logger.warning( "Could not serve " + pathInContext + " from a forward or " + "include. You need to include it in in " + "your appengine-web.xml."); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // passConditionalHeaders will set response headers, and // return true if we also need to send the content. if (included || staticFileUtils.passConditionalHeaders(request, response, resource)) { staticFileUtils.sendData(request, response, included, resource); } } } } finally { if (resource != null) { resource.release(); } } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } /** * Get Resource to serve. * @param pathInContext The path to find a resource for. * @return The resource to serve. Can be null. */ private Resource getResource(String pathInContext) { try { if (resourceBase != null) { return resourceBase.addPath(pathInContext); } } catch (IOException ex) { logger.log(Level.WARNING, "Could not find: " + pathInContext, ex); } return null; } /** * Finds a matching welcome file for the supplied path and, if * found, serves it to the user. This will be the first entry in * the list of configured {@link #welcomeFiles welcome files} that * exists within the directory referenced by the path. If the * resource is not a directory, or no matching file is found, then * null is returned. The list of welcome files is read * from the {@link ContextHandler} for this servlet, or * "index.jsp" , "index.html" if that is * null. * * @return true if a welcome file was served, false otherwise * @throws IOException * @throws MalformedURLException */ private boolean maybeServeWelcomeFile(String path, boolean included, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (welcomeFiles == null) { return false; } // Add a slash for matching purposes. If we needed this slash, we // are not doing an include, and we're not going to redirect // somewhere else we'll redirect the user to add it later. if (!path.endsWith(URIUtil.SLASH)) { path += URIUtil.SLASH; } AppEngineWebXml appEngineWebXml = (AppEngineWebXml) getServletContext().getAttribute( "com.google.appengine.tools.development.appEngineWebXml"); ContextHandler.Context context = (ContextHandler.Context) getServletContext(); ServletHandler handler = ((WebAppContext) context.getContextHandler()).getServletHandler(); MappedResource defaultEntry = handler.getHolderEntry("/"); MappedResource jspEntry = handler.getHolderEntry("/foo.jsp"); // Search for dynamic welcome files. for (String welcomeName : welcomeFiles) { String welcomePath = path + welcomeName; String relativePath = welcomePath.substring(1); MappedResource entry = handler.getHolderEntry(welcomePath); if (!Objects.equals(entry, defaultEntry) && !Objects.equals(entry, jspEntry)) { // It's a path mapped to a servlet. Forward to it. RequestDispatcher dispatcher = request.getRequestDispatcher(path + welcomeName); return staticFileUtils.serveWelcomeFileAsForward(dispatcher, included, request, response); } Resource welcomeFile = getResource(path + welcomeName); if (welcomeFile != null && welcomeFile.exists()) { if (!Objects.equals(entry, defaultEntry)) { RequestDispatcher dispatcher = request.getRequestDispatcher(path + welcomeName); return staticFileUtils.serveWelcomeFileAsForward(dispatcher, included, request, response); } if (appEngineWebXml.includesResource(relativePath)) { // It's a resource file. Forward to it. RequestDispatcher dispatcher = request.getRequestDispatcher(path + welcomeName); return staticFileUtils.serveWelcomeFileAsForward(dispatcher, included, request, response); } } RequestDispatcher namedDispatcher = context.getNamedDispatcher(welcomeName); if (namedDispatcher != null) { // It's a servlet name (allowed by Servlet 2.4 spec). We have // to forward to it. return staticFileUtils.serveWelcomeFileAsForward(namedDispatcher, included, request, response); } } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy