1 // Copyright 2008 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.tools
.development
;
5 import com
.google
.appengine
.tools
.info
.SdkInfo
;
6 import com
.google
.apphosting
.utils
.io
.IoUtil
;
7 import com
.google
.apphosting
.utils
.jetty
.AppEngineWebAppContext
;
9 import org
.mortbay
.jetty
.security
.ConstraintMapping
;
12 import java
.io
.IOException
;
13 import java
.util
.Enumeration
;
14 import java
.util
.List
;
15 import java
.util
.logging
.Logger
;
17 import javax
.servlet
.ServletException
;
18 import javax
.servlet
.http
.HttpServletRequest
;
19 import javax
.servlet
.http
.HttpServletResponse
;
22 * An AppEngineWebAppContext for the DevAppServer.
25 public class DevAppEngineWebAppContext
extends AppEngineWebAppContext
{
27 private static final Logger logger
=
28 Logger
.getLogger(DevAppEngineWebAppContext
.class.getName());
30 private static final String JASPER_SERVLET_CLASSPATH
= "org.apache.catalina.jsp_classpath";
32 private static final String X_GOOGLE_DEV_APPSERVER_SKIPADMINCHECK
=
33 "X-Google-DevAppserver-SkipAdminCheck";
35 private static final String SKIP_ADMIN_CHECK_ATTR
=
36 "com.google.apphosting.internal.SkipAdminCheck";
38 private final Object transportGuaranteeLock
= new Object();
39 private boolean transportGuaranteesDisabled
= false;
41 public DevAppEngineWebAppContext(File appDir
, File externalResourceDir
, String serverInfo
,
42 ApiProxyLocal apiProxyLocal
) {
43 super(appDir
, serverInfo
);
45 setAttribute(JASPER_SERVLET_CLASSPATH
, buildClasspath());
47 _scontext
.setAttribute("com.google.appengine.devappserver.ApiProxyLocal", apiProxyLocal
);
49 if (externalResourceDir
!= null) {
52 setBaseResource(new ExtendedRootResource(getBaseResource(), externalResourceDir
));
53 } catch (IOException e
) {
54 throw new RuntimeException(e
);
60 public void handle(String target
, HttpServletRequest request
, HttpServletResponse response
,
61 int dispatch
) throws IOException
, ServletException
{
63 if (hasSkipAdminCheck(request
)) {
64 request
.setAttribute(SKIP_ADMIN_CHECK_ATTR
, Boolean
.TRUE
);
67 disableTransportGuarantee();
69 System
.setProperty("devappserver-thread-" + Thread
.currentThread().getName(), "true");
71 super.handle(target
, request
, response
, dispatch
);
73 System
.clearProperty("devappserver-thread-" + Thread
.currentThread().getName());
78 * Returns true if the X-Google-Internal-SkipAdminCheck header is
79 * present. There is nothing preventing usercode from setting this header
80 * and circumventing dev appserver security, but the dev appserver was not
81 * designed to be secure.
83 private boolean hasSkipAdminCheck(HttpServletRequest request
) {
84 for (Enumeration
<?
> headerNames
= request
.getHeaderNames(); headerNames
.hasMoreElements(); ) {
85 String name
= (String
) headerNames
.nextElement();
86 if (name
.equalsIgnoreCase(X_GOOGLE_DEV_APPSERVER_SKIPADMINCHECK
)) {
94 * Builds a classpath up for the webapp for JSP compilation.
96 private String
buildClasspath() {
97 StringBuffer classpath
= new StringBuffer();
99 for (File f
: SdkInfo
.getSharedLibFiles()) {
100 classpath
.append(f
.getAbsolutePath());
101 classpath
.append(File
.pathSeparatorChar
);
104 String webAppPath
= getWar();
106 classpath
.append(webAppPath
+ File
.separator
+ "classes" + File
.pathSeparatorChar
);
108 List
<File
> files
= IoUtil
.getFilesAndDirectories(new File(webAppPath
,"lib"));
109 for (File f
: files
) {
110 if (f
.isFile() && f
.getName().endsWith(".jar")) {
111 classpath
.append(f
.getAbsolutePath());
112 classpath
.append(File
.pathSeparatorChar
);
116 return classpath
.toString();
120 * The first time this method is called it will walk through the
121 * constraint mappings on the current SecurityHandler and disable
122 * any transport guarantees that have been set. This is required to
123 * disable SSL requirements in the DevAppServer because it does not
126 private void disableTransportGuarantee() {
127 synchronized (transportGuaranteeLock
) {
128 if (!transportGuaranteesDisabled
) {
129 if (getSecurityHandler() != null) {
130 ConstraintMapping
[] mappings
= getSecurityHandler().getConstraintMappings();
131 if (mappings
!= null) {
132 for (ConstraintMapping mapping
: mappings
) {
133 if (mapping
.getConstraint().getDataConstraint() > 0) {
134 logger
.info("Ignoring <transport-guarantee> for " + mapping
.getPathSpec() +
135 " as the SDK does not support HTTPS. It will still be used" +
136 " when you upload your application.");
137 mapping
.getConstraint().setDataConstraint(0);
143 transportGuaranteesDisabled
= true;