Skip to content

Commit

Permalink
Merge pull request #188 from GoogleCloudPlatform:devappserver-debugging
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 633438786
Change-Id: I8e9d23b47737d144580b520e315e7b7230f7f728
  • Loading branch information
gae-java-bot committed May 14, 2024
2 parents 9141da6 + f48c889 commit cb3c8b3
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.common.base.Preconditions;
import com.google.common.html.HtmlEscapers;
import com.google.common.net.HttpHeaders;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
Expand Down Expand Up @@ -728,7 +729,7 @@ public void sendError(int sc, String msg) throws IOException {
checkNotCommitted();
// This has to be re-implemented to avoid committing the response.
setStatus(sc, msg);
setErrorBody(sc + " " + HtmlEscapers.htmlEscaper().escape(msg));
setErrorBody(sc + " " + (msg == null ? "" : HtmlEscapers.htmlEscaper().escape(msg)));
}

/** Sets the response body to an HTML page with an error message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
Expand Down Expand Up @@ -728,8 +729,8 @@ public void sendError(int sc) throws IOException {
public void sendError(int sc, String msg) throws IOException {
checkNotCommitted();
// This has to be re-implemented to avoid committing the response.
super.sendError(sc, msg);
setErrorBody(sc + " " + HtmlEscapers.htmlEscaper().escape(msg));
setStatus(sc);
setErrorBody(sc + " " + (msg == null ? "" : HtmlEscapers.htmlEscaper().escape(msg)));
}

/** Sets the response body to an HTML page with an error message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.appengine.tools.info;

import com.google.common.base.Joiner;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
Expand Down Expand Up @@ -191,6 +192,24 @@ public List<URL> getSharedJspLibs() {
return Collections.unmodifiableList(toURLs(getSharedJspLibFiles()));
}

private File getJetty12Jar(String fileNamePattern) {
File path = new File(sdkRoot, JETTY12_HOME_LIB_PATH + File.separator);

if (!path.exists()) {
throw new IllegalArgumentException("Unable to find " + path.getAbsolutePath());
}
for (File f : listFiles(path)) {
if (f.getName().endsWith(".jar")) {
// All but CDI jar. All the tests are still passing without CDI that should not be exposed
// in our runtime (private Jetty dependency we do not want to expose to the customer).
if (f.getName().contains(fileNamePattern)) {
return f;
}
}
}
throw new IllegalArgumentException("Unable to find " + fileNamePattern + " at " + path.getAbsolutePath());
}

private List<File> getJetty12Jars(String subDir) {
File path = new File(sdkRoot, JETTY12_HOME_LIB_PATH + File.separator + subDir);

Expand Down Expand Up @@ -219,10 +238,12 @@ List<File> getJetty12JspJars() {
if (Boolean.getBoolean("appengine.use.EE10")) {
List<File> lf = getJetty12Jars("ee10-apache-jsp");
lf.addAll(getJetty12Jars("ee10-glassfish-jstl"));
lf.add(getJetty12Jar("ee10-servlet-"));
return lf;
}
List<File> lf = getJetty12Jars("ee8-apache-jsp");
lf.addAll(getJetty12Jars("ee8-glassfish-jstl"));
lf.add(getJetty12Jar("ee8-servlet-"));
return lf;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import java.util.logging.Logger;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHandler;
import org.eclipse.jetty.http.pathmap.MatchedResource;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
Expand All @@ -57,6 +57,7 @@ public class LocalResourceFileServlet extends HttpServlet {
private Resource resourceBase;
private String[] welcomeFiles;
private String resourceRoot;
private String defaultServletName;

/**
* Initialize the servlet by extracting some useful configuration
Expand All @@ -73,6 +74,12 @@ public void init() throws ServletException {
ServletContextHandler.getServletContextHandler(servletContext);
welcomeFiles = contextHandler.getWelcomeFiles();

ServletMapping servletMapping = contextHandler.getServletHandler().getServletMapping("/");
if (servletMapping == null) {
throw new ServletException("No servlet mapping found");
}
defaultServletName = servletMapping.getServletName();

AppEngineWebXml appEngineWebXml =
(AppEngineWebXml)
servletContext.getAttribute("com.google.appengine.tools.development.appEngineWebXml");
Expand Down Expand Up @@ -254,24 +261,23 @@ private boolean maybeServeWelcomeFile(
ServletContext context = getServletContext();
ServletContextHandler contextHandler = ServletContextHandler.getServletContextHandler(context);
ServletHandler handler = contextHandler.getServletHandler();
MatchedResource<ServletHandler.MappedServlet> defaultEntry = handler.getMatchedServlet("/");
MatchedResource<ServletHandler.MappedServlet> jspEntry = handler.getMatchedServlet("/foo.jsp");
ServletHandler.MappedServlet jspEntry = handler.getMappedServlet("/foo.jsp");

// Search for dynamic welcome files.
for (String welcomeName : welcomeFiles) {
String welcomePath = path + welcomeName;
String relativePath = welcomePath.substring(1);

MatchedResource<ServletHandler.MappedServlet> entry = handler.getMatchedServlet(welcomePath);
if (!Objects.equals(entry, defaultEntry) && !Objects.equals(entry, jspEntry)) {
ServletHandler.MappedServlet mappedServlet = handler.getMappedServlet(welcomePath);
if (!Objects.equals(mappedServlet.getServletHolder().getName(), defaultServletName) && !Objects.equals(mappedServlet, 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)) {
if (!Objects.equals(mappedServlet.getServletHolder().getName(), defaultServletName)) {
RequestDispatcher dispatcher = request.getRequestDispatcher(path + welcomeName);
return staticFileUtils.serveWelcomeFileAsForward(dispatcher, included, request, response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ protected void startWebapp() throws Exception {
// - Removed deprecated filters and servlets
// - Ensure known runtime filters/servlets are instantiated from this classloader
// - Ensure known runtime mappings exist.

ServletHandler servletHandler = getServletHandler();
TrimmedFilters trimmedFilters =
new TrimmedFilters(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,63 +239,56 @@ public void doStart() throws Exception {
}

@Override
protected void startContext() throws Exception {
ServletHandler servletHandler = getServletHandler();
getServletHandler().addListener(new ListenerHolder() {
@Override
public void doStart() throws Exception {

// This Listener doStart is called after the web.xml metadata has been resolved, so we can
// clean configuration here:
// - Removed deprecated filters and servlets
// - Ensure known runtime filters/servlets are instantiated from this classloader
// - Ensure known runtime mappings exist.
setListener(new EventListener() {});

TrimmedFilters trimmedFilters =
new TrimmedFilters(
servletHandler.getFilters(),
servletHandler.getFilterMappings(),
DEPRECATED_SERVLETS_FILTERS);
trimmedFilters.ensure(
"CloudSqlConnectionCleanupFilter", JdbcMySqlConnectionCleanupFilter.class, "/*");

TrimmedServlets trimmedServlets =
new TrimmedServlets(
servletHandler.getServlets(),
servletHandler.getServletMappings(),
DEPRECATED_SERVLETS_FILTERS);
trimmedServlets.ensure("_ah_warmup", WarmupServlet.class, "/_ah/warmup");
trimmedServlets.ensure(
"_ah_sessioncleanup", SessionCleanupServlet.class, "/_ah/sessioncleanup");
trimmedServlets.ensure(
"_ah_queue_deferred", DeferredTaskServlet.class, "/_ah/queue/__deferred__");
trimmedServlets.ensure("_ah_snapshot", SnapshotServlet.class, "/_ah/snapshot");
trimmedServlets.ensure("_ah_default", ResourceFileServlet.class, "/");
trimmedServlets.ensure("default", NamedDefaultServlet.class);
trimmedServlets.ensure("jsp", NamedJspServlet.class);

trimmedServlets.instantiateJettyServlets();
trimmedFilters.instantiateJettyFilters();
instantiateJettyListeners();

servletHandler.setFilters(trimmedFilters.getHolders());
servletHandler.setFilterMappings(trimmedFilters.getMappings());
servletHandler.setServlets(trimmedServlets.getHolders());
servletHandler.setServletMappings(trimmedServlets.getMappings());
servletHandler.setAllowDuplicateMappings(true);

// Protect deferred task queue with constraint
ConstraintSecurityHandler security = getChildHandlerByClass(ConstraintSecurityHandler.class);
ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(new ServletConstraint("deferred_queue", "admin"));
cm.setPathSpec("/_ah/queue/__deferred__");
security.addConstraintMapping(cm);
}
});

// continue starting the webapp
super.startContext();
protected void startWebapp() throws Exception {
// This Listener doStart is called after the web.xml metadata has been resolved, so we can
// clean configuration here:
// - Removed deprecated filters and servlets
// - Ensure known runtime filters/servlets are instantiated from this classloader
// - Ensure known runtime mappings exist.
ServletHandler servletHandler = getServletHandler();
TrimmedFilters trimmedFilters =
new TrimmedFilters(
servletHandler.getFilters(),
servletHandler.getFilterMappings(),
DEPRECATED_SERVLETS_FILTERS);
trimmedFilters.ensure(
"CloudSqlConnectionCleanupFilter", JdbcMySqlConnectionCleanupFilter.class, "/*");

TrimmedServlets trimmedServlets =
new TrimmedServlets(
servletHandler.getServlets(),
servletHandler.getServletMappings(),
DEPRECATED_SERVLETS_FILTERS);
trimmedServlets.ensure("_ah_warmup", WarmupServlet.class, "/_ah/warmup");
trimmedServlets.ensure(
"_ah_sessioncleanup", SessionCleanupServlet.class, "/_ah/sessioncleanup");
trimmedServlets.ensure(
"_ah_queue_deferred", DeferredTaskServlet.class, "/_ah/queue/__deferred__");
trimmedServlets.ensure("_ah_snapshot", SnapshotServlet.class, "/_ah/snapshot");
trimmedServlets.ensure("_ah_default", ResourceFileServlet.class, "/");
trimmedServlets.ensure("default", NamedDefaultServlet.class);
trimmedServlets.ensure("jsp", NamedJspServlet.class);

trimmedServlets.instantiateJettyServlets();
trimmedFilters.instantiateJettyFilters();
instantiateJettyListeners();

servletHandler.setFilters(trimmedFilters.getHolders());
servletHandler.setFilterMappings(trimmedFilters.getMappings());
servletHandler.setServlets(trimmedServlets.getHolders());
servletHandler.setServletMappings(trimmedServlets.getMappings());
servletHandler.setAllowDuplicateMappings(true);

// Protect deferred task queue with constraint
ConstraintSecurityHandler security = getChildHandlerByClass(ConstraintSecurityHandler.class);
ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(new ServletConstraint("deferred_queue", "admin"));
cm.setPathSpec("/_ah/queue/__deferred__");
security.addConstraintMapping(cm);


// continue starting the webapp
super.startWebapp();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ public static void populateErrorResponse(Response resp, String errMsg, Callback
try (OutputStream outstr = Content.Sink.asOutputStream(resp)) {
PrintWriter writer = new PrintWriter(outstr);
writer.print("<html><head><title>Server Error</title></head>");
writer.print("<body>" + HtmlEscapers.htmlEscaper().escape(errMsg) + "</body></html>");
String escapedMessage = (errMsg == null) ? "" : HtmlEscapers.htmlEscaper().escape(errMsg);
writer.print("<body>" + escapedMessage + "</body></html>");
writer.close();
callback.succeeded();
} catch (Throwable t) {
Expand Down

0 comments on commit cb3c8b3

Please sign in to comment.