diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index fe97fa31d..ecbbc633c 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -15,4 +15,4 @@
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.1
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
diff --git a/README.md b/README.md
index 824fa076c..69853c857 100644
--- a/README.md
+++ b/README.md
@@ -69,7 +69,7 @@ Source code for all public APIs for com.google.appengine.api.* packages.
com.google.appengineappengine-api-1.0-sdk
- 2.0.27
+ 2.0.28javax.servlet
@@ -89,7 +89,7 @@ Source code for all public APIs for com.google.appengine.api.* packages.
com.google.appengineappengine-api-1.0-sdk
- 2.0.27
+ 2.0.28javax.servlet
@@ -188,7 +188,7 @@ Source code for remote APIs for App Engine.
com.google.appengineappengine-remote-api
- 2.0.27
+ 2.0.28
```
@@ -211,7 +211,7 @@ We moved `com.google.appengine.api.memcache.stdimpl` and its old dependency
com.google.appengineappengine-api-legacy.jar/artifactId>
- 2.0.27
+ 2.0.28
```
@@ -226,19 +226,19 @@ We moved `com.google.appengine.api.memcache.stdimpl` and its old dependency
com.google.appengineappengine-testing
- 2.0.27
+ 2.0.28testcom.google.appengineappengine-api-stubs
- 2.0.27
+ 2.0.28testcom.google.appengineappengine-tools-sdk
- 2.0.27
+ 2.0.28test
```
diff --git a/TRYLATESTBITSINPROD.md b/TRYLATESTBITSINPROD.md
index 8d937eb1a..9f4a4eea7 100644
--- a/TRYLATESTBITSINPROD.md
+++ b/TRYLATESTBITSINPROD.md
@@ -46,12 +46,12 @@ top of your web application and change the entrypoint to boot with these jars in
mvn clean install
```
-Let's assume the current built version is `2.0.28-SNAPSHOT`.
+Let's assume the current built version is `2.0.29-SNAPSHOT`.
Add the dependency for the GAE runtime jars in your application pom.xml file:
```
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTtarget/${project.artifactId}-${project.version}
...
diff --git a/api/pom.xml b/api/pom.xml
index 87f1a692a..25544e889 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -21,7 +21,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTtrue
@@ -239,7 +239,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 3.6.3
+ 3.7.0com.microsoft.doclet.DocFxDocletfalse
diff --git a/api_dev/pom.xml b/api_dev/pom.xml
index 057f8ce52..203d807b0 100644
--- a/api_dev/pom.xml
+++ b/api_dev/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/api_legacy/pom.xml b/api_legacy/pom.xml
index 47d744cf6..3f57294f9 100644
--- a/api_legacy/pom.xml
+++ b/api_legacy/pom.xml
@@ -22,7 +22,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/appengine-api-1.0-sdk/pom.xml b/appengine-api-1.0-sdk/pom.xml
index 6b59f9deb..7050400ee 100644
--- a/appengine-api-1.0-sdk/pom.xml
+++ b/appengine-api-1.0-sdk/pom.xml
@@ -20,7 +20,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjarAppEngine :: appengine-api-1.0-sdk
diff --git a/appengine-api-stubs/pom.xml b/appengine-api-stubs/pom.xml
index 3f7a96fc4..efd0ea8e6 100644
--- a/appengine-api-stubs/pom.xml
+++ b/appengine-api-stubs/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/appengine_init/pom.xml b/appengine_init/pom.xml
index 3fe10f5b6..8128f855a 100644
--- a/appengine_init/pom.xml
+++ b/appengine_init/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/appengine_init/src/main/java/com/google/appengine/init/AppEngineWebXmlInitialParse.java b/appengine_init/src/main/java/com/google/appengine/init/AppEngineWebXmlInitialParse.java
index d9810320f..01e115d61 100644
--- a/appengine_init/src/main/java/com/google/appengine/init/AppEngineWebXmlInitialParse.java
+++ b/appengine_init/src/main/java/com/google/appengine/init/AppEngineWebXmlInitialParse.java
@@ -46,6 +46,7 @@ public final class AppEngineWebXmlInitialParse {
/** a formatted build timestamp with pattern yyyy-MM-dd'T'HH:mm:ssXXX */
public static final String BUILD_TIMESTAMP;
+
private static final String BUILD_VERSION;
private static final Properties BUILD_PROPERTIES = new Properties();
@@ -56,7 +57,7 @@ public final class AppEngineWebXmlInitialParse {
"/com/google/appengine/init/build.properties")) {
BUILD_PROPERTIES.load(inputStream);
} catch (Exception ok) {
- // File not there; that's fine, just continue.
+ // File not there; that's fine, just continue.
}
GIT_HASH = BUILD_PROPERTIES.getProperty("buildNumber", "unknown");
System.setProperty("appengine.git.hash", GIT_HASH);
@@ -124,7 +125,9 @@ private void setAppEngineUseProperties(final XMLEventReader reader) throws XMLSt
System.setProperty("appengine.use.HttpConnector", value);
} else if (prop.equalsIgnoreCase("appengine.use.allheaders")) {
System.setProperty("appengine.use.allheaders", value);
- }
+ } else if (prop.equalsIgnoreCase("appengine.ignore.responseSizeLimit")) {
+ System.setProperty("appengine.ignore.responseSizeLimit", value);
+ }
}
}
}
diff --git a/appengine_jsr107/pom.xml b/appengine_jsr107/pom.xml
index 5bf487b87..9f8b380ed 100644
--- a/appengine_jsr107/pom.xml
+++ b/appengine_jsr107/pom.xml
@@ -24,7 +24,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT
diff --git a/appengine_resources/pom.xml b/appengine_resources/pom.xml
index 227d12183..c73d8dc44 100644
--- a/appengine_resources/pom.xml
+++ b/appengine_resources/pom.xml
@@ -21,7 +21,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjarAppEngine :: appengine-resources
diff --git a/appengine_setup/apiserver_local/pom.xml b/appengine_setup/apiserver_local/pom.xml
index 464b0a575..feb8ecd4a 100644
--- a/appengine_setup/apiserver_local/pom.xml
+++ b/appengine_setup/apiserver_local/pom.xml
@@ -20,7 +20,7 @@
com.google.appengineappengine_setup
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT4.0.0
@@ -36,7 +36,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 3.2.5
+ 3.3.0org.apache.maven.plugins
diff --git a/appengine_setup/pom.xml b/appengine_setup/pom.xml
index 4fe52d1f5..931a69538 100644
--- a/appengine_setup/pom.xml
+++ b/appengine_setup/pom.xml
@@ -24,7 +24,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTapiserver_local
diff --git a/appengine_setup/test/pom.xml b/appengine_setup/test/pom.xml
index 2b3c12a02..1e7326595 100644
--- a/appengine_setup/test/pom.xml
+++ b/appengine_setup/test/pom.xml
@@ -20,7 +20,7 @@
com.google.appengineappengine_setup
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT4.0.0com.google.appengine
diff --git a/appengine_setup/test/src/test/java/com/google/appengine/setup/test/Jetty12TestAppTest.java b/appengine_setup/test/src/test/java/com/google/appengine/setup/test/Jetty12TestAppTest.java
index b841a0d06..ed745ebd0 100644
--- a/appengine_setup/test/src/test/java/com/google/appengine/setup/test/Jetty12TestAppTest.java
+++ b/appengine_setup/test/src/test/java/com/google/appengine/setup/test/Jetty12TestAppTest.java
@@ -26,6 +26,6 @@ protected String appName() {
@Override
protected String relativePathForUserApplicationJar() {
return "../testapps/jetty12_testapp/target/"
- + "jetty12_testapp-2.0.28-SNAPSHOT-jar-with-dependencies.jar";
+ + "jetty12_testapp-2.0.29-SNAPSHOT-jar-with-dependencies.jar";
}
}
diff --git a/appengine_setup/test/src/test/java/com/google/appengine/setup/test/SpringBootTestAppTest.java b/appengine_setup/test/src/test/java/com/google/appengine/setup/test/SpringBootTestAppTest.java
index df8590a8f..ab1d27c0d 100644
--- a/appengine_setup/test/src/test/java/com/google/appengine/setup/test/SpringBootTestAppTest.java
+++ b/appengine_setup/test/src/test/java/com/google/appengine/setup/test/SpringBootTestAppTest.java
@@ -25,6 +25,6 @@ protected String appName() {
@Override
protected String relativePathForUserApplicationJar() {
- return "../testapps/springboot_testapp/target/" + "springboot_testapp-2.0.28-SNAPSHOT.jar";
+ return "../testapps/springboot_testapp/target/" + "springboot_testapp-2.0.29-SNAPSHOT.jar";
}
}
diff --git a/appengine_setup/test/src/test/java/com/google/appengine/setup/test/util/TestUtil.java b/appengine_setup/test/src/test/java/com/google/appengine/setup/test/util/TestUtil.java
index f7582607d..c6914d34d 100644
--- a/appengine_setup/test/src/test/java/com/google/appengine/setup/test/util/TestUtil.java
+++ b/appengine_setup/test/src/test/java/com/google/appengine/setup/test/util/TestUtil.java
@@ -62,7 +62,7 @@ public static HttpClient initializeHttpClient(int timeoutMillis) {
@SneakyThrows
public static Process initializeHttpApiServer() {
String apiServerRelativeJarPath =
- "../apiserver_local/target/" + "apiserver_local-2.0.28-SNAPSHOT-jar-with-dependencies.jar";
+ "../apiserver_local/target/" + "apiserver_local-2.0.29-SNAPSHOT-jar-with-dependencies.jar";
File currentDirectory = new File("").getAbsoluteFile();
File apiServerJar = new File(currentDirectory, apiServerRelativeJarPath);
ImmutableList processArgs = ImmutableList.builder()
diff --git a/appengine_setup/testapps/jetty12_testapp/pom.xml b/appengine_setup/testapps/jetty12_testapp/pom.xml
index f7e00cbc9..8ac7671c5 100644
--- a/appengine_setup/testapps/jetty12_testapp/pom.xml
+++ b/appengine_setup/testapps/jetty12_testapp/pom.xml
@@ -20,20 +20,20 @@
testappscom.google.appengine
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT4.0.0com.google.appengine.setup.testappsjetty12_testapp
- 12.0.10
+ 12.0.111.9.22.1com.google.appengine.setup.testappstestapps_common
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTorg.eclipse.jetty
@@ -106,7 +106,7 @@
srirammahavadi-devGCLOUD_CONFIGtrue
- ${project.build.directory}/jetty11_testapp-2.0.28-SNAPSHOT-jar-with-dependencies.jar
+ ${project.build.directory}/jetty11_testapp-2.0.29-SNAPSHOT-jar-with-dependencies.jar
diff --git a/appengine_setup/testapps/pom.xml b/appengine_setup/testapps/pom.xml
index 31cc88b24..7a9ceb0ee 100644
--- a/appengine_setup/testapps/pom.xml
+++ b/appengine_setup/testapps/pom.xml
@@ -20,7 +20,7 @@
com.google.appengineappengine_setup
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT4.0.0com.google.appengine
diff --git a/appengine_setup/testapps/springboot_testapp/pom.xml b/appengine_setup/testapps/springboot_testapp/pom.xml
index 521c48886..645425fac 100644
--- a/appengine_setup/testapps/springboot_testapp/pom.xml
+++ b/appengine_setup/testapps/springboot_testapp/pom.xml
@@ -25,7 +25,7 @@
com.google.appengine.setup.testappsspringboot_testapp
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTspringboot_testappDemo project for Spring Boot
@@ -44,12 +44,12 @@
com.google.appengineappengine-api-1.0-sdk
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTcom.google.appengine.setup.testappstestapps_common
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/appengine_setup/testapps/testapps_common/pom.xml b/appengine_setup/testapps/testapps_common/pom.xml
index fbfe9082a..453dce47c 100644
--- a/appengine_setup/testapps/testapps_common/pom.xml
+++ b/appengine_setup/testapps/testapps_common/pom.xml
@@ -20,7 +20,7 @@
testappscom.google.appengine
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT4.0.0com.google.appengine.setup.testapps
diff --git a/appengine_testing/pom.xml b/appengine_testing/pom.xml
index d01756f89..bdf22a17c 100644
--- a/appengine_testing/pom.xml
+++ b/appengine_testing/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/appengine_testing_tests/pom.xml b/appengine_testing_tests/pom.xml
index 077391748..9e9abaf55 100644
--- a/appengine_testing_tests/pom.xml
+++ b/appengine_testing_tests/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/applications/pom.xml b/applications/pom.xml
index 35efc7510..02a1f0bc2 100644
--- a/applications/pom.xml
+++ b/applications/pom.xml
@@ -22,7 +22,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTpom
diff --git a/applications/proberapp/pom.xml b/applications/proberapp/pom.xml
index 1d51bc57d..59488bfe5 100644
--- a/applications/proberapp/pom.xml
+++ b/applications/proberapp/pom.xml
@@ -27,7 +27,7 @@
com.google.appengineapplications
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT
@@ -38,7 +38,7 @@
us-central1prober-userprober_connectivity_test_database
-
+ 2.50.0${project.version}UTF-8target/${project.artifactId}-${project.version}
@@ -58,22 +58,22 @@
com.google.cloudgoogle-cloud-spanner
- 6.68.1
+ 6.71.0com.google.apigax
- 2.49.0
+ ${gax.version}com.google.apigax-httpjson
- 2.49.0
+ ${gax.version}com.google.apigax-grpc
- 2.49.0
+ ${gax.version}com.google.api-client
@@ -86,27 +86,27 @@
com.google.cloudgoogle-cloud-bigquery
- 2.40.2
+ 2.41.0com.google.cloudgoogle-cloud-core
- 2.39.0
+ 2.40.0com.google.cloudgoogle-cloud-datastore
- 2.20.1
+ 2.20.2com.google.cloudgoogle-cloud-logging
- 3.18.0
+ 3.19.0com.google.cloudgoogle-cloud-storage
- 2.39.0
+ 2.40.1com.google.cloud.sql
@@ -281,4 +281,4 @@
-
+
\ No newline at end of file
diff --git a/applications/springboot/pom.xml b/applications/springboot/pom.xml
index 7235d6700..a20b7bea2 100644
--- a/applications/springboot/pom.xml
+++ b/applications/springboot/pom.xml
@@ -24,7 +24,7 @@
com.google.appengineapplications
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/devappservertests/pom.xml b/e2etests/devappservertests/pom.xml
index 5424e4683..adb712da0 100644
--- a/e2etests/devappservertests/pom.xml
+++ b/e2etests/devappservertests/pom.xml
@@ -22,7 +22,7 @@
com.google.appenginee2etests
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/e2etests/pom.xml b/e2etests/pom.xml
index 222b95de0..56217a3a0 100644
--- a/e2etests/pom.xml
+++ b/e2etests/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTAppEngine :: e2e testspom
diff --git a/e2etests/stagingtests/pom.xml b/e2etests/stagingtests/pom.xml
index 98badecbb..ad84260b7 100644
--- a/e2etests/stagingtests/pom.xml
+++ b/e2etests/stagingtests/pom.xml
@@ -22,7 +22,7 @@
com.google.appenginee2etests
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/e2etests/testlocalapps/allinone/pom.xml b/e2etests/testlocalapps/allinone/pom.xml
index 148a3a361..fe21ddd06 100644
--- a/e2etests/testlocalapps/allinone/pom.xml
+++ b/e2etests/testlocalapps/allinone/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/allinone_jakarta/pom.xml b/e2etests/testlocalapps/allinone_jakarta/pom.xml
index a75ea6071..3615f5c53 100644
--- a/e2etests/testlocalapps/allinone_jakarta/pom.xml
+++ b/e2etests/testlocalapps/allinone_jakarta/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/badcron/pom.xml b/e2etests/testlocalapps/badcron/pom.xml
index 75d72820f..b5125568f 100644
--- a/e2etests/testlocalapps/badcron/pom.xml
+++ b/e2etests/testlocalapps/badcron/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/bundle_standard/pom.xml b/e2etests/testlocalapps/bundle_standard/pom.xml
index 0ddf03824..0817deee3 100644
--- a/e2etests/testlocalapps/bundle_standard/pom.xml
+++ b/e2etests/testlocalapps/bundle_standard/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml b/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml
index cffc1787d..5944bad9c 100644
--- a/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml
+++ b/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml
@@ -25,7 +25,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml b/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml
index e345fc4a8..95f096f66 100644
--- a/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml
+++ b/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml b/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml
index 9b3037d10..65cbdd2b3 100644
--- a/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml
+++ b/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml b/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml
index 01c248a95..bab7551e0 100644
--- a/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml
+++ b/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml b/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml
index 22f30dfe8..8b31d2e32 100644
--- a/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml
+++ b/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml b/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml
index ec50eb466..ce96f93d6 100644
--- a/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml
+++ b/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml b/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml
index 8f4351138..5f734c59f 100644
--- a/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml
+++ b/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/cron-two-max-doublings/pom.xml b/e2etests/testlocalapps/cron-two-max-doublings/pom.xml
index baa5aa0a3..34f1e9ae6 100644
--- a/e2etests/testlocalapps/cron-two-max-doublings/pom.xml
+++ b/e2etests/testlocalapps/cron-two-max-doublings/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/http-headers/pom.xml b/e2etests/testlocalapps/http-headers/pom.xml
index 775ff63d9..b49f4bd75 100644
--- a/e2etests/testlocalapps/http-headers/pom.xml
+++ b/e2etests/testlocalapps/http-headers/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/java8-jar/pom.xml b/e2etests/testlocalapps/java8-jar/pom.xml
index f12420f83..e5af26ddd 100644
--- a/e2etests/testlocalapps/java8-jar/pom.xml
+++ b/e2etests/testlocalapps/java8-jar/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/java8-no-webxml/pom.xml b/e2etests/testlocalapps/java8-no-webxml/pom.xml
index 5715bee0d..cbeca5b4f 100644
--- a/e2etests/testlocalapps/java8-no-webxml/pom.xml
+++ b/e2etests/testlocalapps/java8-no-webxml/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/pom.xml b/e2etests/testlocalapps/pom.xml
index c93a49d84..6ef4ffadf 100644
--- a/e2etests/testlocalapps/pom.xml
+++ b/e2etests/testlocalapps/pom.xml
@@ -22,7 +22,7 @@
com.google.appenginee2etests
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTpom
diff --git a/e2etests/testlocalapps/sample-badaeweb/pom.xml b/e2etests/testlocalapps/sample-badaeweb/pom.xml
index a624a53d3..d7f4930cc 100644
--- a/e2etests/testlocalapps/sample-badaeweb/pom.xml
+++ b/e2etests/testlocalapps/sample-badaeweb/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml b/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml
index eeb3c72b2..b13938050 100644
--- a/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml
+++ b/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-baddispatch/pom.xml b/e2etests/testlocalapps/sample-baddispatch/pom.xml
index dcc0c674f..8780b89b7 100644
--- a/e2etests/testlocalapps/sample-baddispatch/pom.xml
+++ b/e2etests/testlocalapps/sample-baddispatch/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-badentrypoint/pom.xml b/e2etests/testlocalapps/sample-badentrypoint/pom.xml
index 305399c62..9ea70cb30 100644
--- a/e2etests/testlocalapps/sample-badentrypoint/pom.xml
+++ b/e2etests/testlocalapps/sample-badentrypoint/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-badindexes/pom.xml b/e2etests/testlocalapps/sample-badindexes/pom.xml
index e1ef0e231..69e6bceaf 100644
--- a/e2etests/testlocalapps/sample-badindexes/pom.xml
+++ b/e2etests/testlocalapps/sample-badindexes/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-badruntimechannel/pom.xml b/e2etests/testlocalapps/sample-badruntimechannel/pom.xml
index da7257a79..c715f7b61 100644
--- a/e2etests/testlocalapps/sample-badruntimechannel/pom.xml
+++ b/e2etests/testlocalapps/sample-badruntimechannel/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-badweb/pom.xml b/e2etests/testlocalapps/sample-badweb/pom.xml
index 713f3c53d..d844b3f17 100644
--- a/e2etests/testlocalapps/sample-badweb/pom.xml
+++ b/e2etests/testlocalapps/sample-badweb/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-default-auto-ids/pom.xml b/e2etests/testlocalapps/sample-default-auto-ids/pom.xml
index 88c8fed65..4daf5796f 100644
--- a/e2etests/testlocalapps/sample-default-auto-ids/pom.xml
+++ b/e2etests/testlocalapps/sample-default-auto-ids/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml b/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml
index bdcf4e66c..c39523d1d 100644
--- a/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml
+++ b/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-java11/pom.xml b/e2etests/testlocalapps/sample-java11/pom.xml
index c50af8be5..648df504b 100644
--- a/e2etests/testlocalapps/sample-java11/pom.xml
+++ b/e2etests/testlocalapps/sample-java11/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-java17/pom.xml b/e2etests/testlocalapps/sample-java17/pom.xml
index ac9c094c9..d40cafb62 100644
--- a/e2etests/testlocalapps/sample-java17/pom.xml
+++ b/e2etests/testlocalapps/sample-java17/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml b/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml
index 6a004b396..2ade61b1c 100644
--- a/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml
+++ b/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-jspx/pom.xml b/e2etests/testlocalapps/sample-jspx/pom.xml
index 3ca99a668..873102850 100644
--- a/e2etests/testlocalapps/sample-jspx/pom.xml
+++ b/e2etests/testlocalapps/sample-jspx/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml b/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml
index 3429515f4..30432151e 100644
--- a/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml
+++ b/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-missingappid/pom.xml b/e2etests/testlocalapps/sample-missingappid/pom.xml
index 72216b8ef..61b9650cb 100644
--- a/e2etests/testlocalapps/sample-missingappid/pom.xml
+++ b/e2etests/testlocalapps/sample-missingappid/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-nojsps/pom.xml b/e2etests/testlocalapps/sample-nojsps/pom.xml
index c799ac6f1..4898b6a74 100644
--- a/e2etests/testlocalapps/sample-nojsps/pom.xml
+++ b/e2etests/testlocalapps/sample-nojsps/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml b/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml
index 695d46fc0..02f349831 100644
--- a/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml
+++ b/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sample-with-classes/pom.xml b/e2etests/testlocalapps/sample-with-classes/pom.xml
index 24c5b92fd..d15a2f6e3 100644
--- a/e2etests/testlocalapps/sample-with-classes/pom.xml
+++ b/e2etests/testlocalapps/sample-with-classes/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml b/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml
index dfa833388..eb3d42cb9 100644
--- a/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml
+++ b/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sampleapp-backends/pom.xml b/e2etests/testlocalapps/sampleapp-backends/pom.xml
index 499f1fb68..460fa693f 100644
--- a/e2etests/testlocalapps/sampleapp-backends/pom.xml
+++ b/e2etests/testlocalapps/sampleapp-backends/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwarAppEngine :: sampleapp-backends
diff --git a/e2etests/testlocalapps/sampleapp-basic-module/pom.xml b/e2etests/testlocalapps/sampleapp-basic-module/pom.xml
index 474dae6d0..750ccb42c 100644
--- a/e2etests/testlocalapps/sampleapp-basic-module/pom.xml
+++ b/e2etests/testlocalapps/sampleapp-basic-module/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sampleapp-manual-module/pom.xml b/e2etests/testlocalapps/sampleapp-manual-module/pom.xml
index b23b323a0..2e8ef3c30 100644
--- a/e2etests/testlocalapps/sampleapp-manual-module/pom.xml
+++ b/e2etests/testlocalapps/sampleapp-manual-module/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sampleapp-runtime/pom.xml b/e2etests/testlocalapps/sampleapp-runtime/pom.xml
index d590195c6..12f39f0e6 100644
--- a/e2etests/testlocalapps/sampleapp-runtime/pom.xml
+++ b/e2etests/testlocalapps/sampleapp-runtime/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/sampleapp/pom.xml b/e2etests/testlocalapps/sampleapp/pom.xml
index 4727b36b5..5c2ec0951 100644
--- a/e2etests/testlocalapps/sampleapp/pom.xml
+++ b/e2etests/testlocalapps/sampleapp/pom.xml
@@ -25,7 +25,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTAppEngine :: sampleapp
diff --git a/e2etests/testlocalapps/stage-sampleapp/pom.xml b/e2etests/testlocalapps/stage-sampleapp/pom.xml
index 413c254d2..407119451 100644
--- a/e2etests/testlocalapps/stage-sampleapp/pom.xml
+++ b/e2etests/testlocalapps/stage-sampleapp/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/stage-with-staging-options/pom.xml b/e2etests/testlocalapps/stage-with-staging-options/pom.xml
index 17e13fbfb..1a614d55b 100644
--- a/e2etests/testlocalapps/stage-with-staging-options/pom.xml
+++ b/e2etests/testlocalapps/stage-with-staging-options/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwar
diff --git a/e2etests/testlocalapps/xmlorder/pom.xml b/e2etests/testlocalapps/xmlorder/pom.xml
index ce05d581a..45a3a1a01 100644
--- a/e2etests/testlocalapps/xmlorder/pom.xml
+++ b/e2etests/testlocalapps/xmlorder/pom.xml
@@ -24,7 +24,7 @@
com.google.appenginetestlocalapps
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTwarAppEngine :: xmlorder
diff --git a/external/geronimo_javamail/pom.xml b/external/geronimo_javamail/pom.xml
index c74eb1c41..4c690a0e1 100644
--- a/external/geronimo_javamail/pom.xml
+++ b/external/geronimo_javamail/pom.xml
@@ -22,7 +22,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT../../pom.xml
diff --git a/google3/third_party/java_src/appengine_standard/api_compatibility_tests/pom.xml b/google3/third_party/java_src/appengine_standard/api_compatibility_tests/pom.xml
index e94e420da..3873065f4 100644
--- a/google3/third_party/java_src/appengine_standard/api_compatibility_tests/pom.xml
+++ b/google3/third_party/java_src/appengine_standard/api_compatibility_tests/pom.xml
@@ -22,7 +22,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/jetty12_assembly/pom.xml b/jetty12_assembly/pom.xml
index dea1b3764..c8a052c68 100644
--- a/jetty12_assembly/pom.xml
+++ b/jetty12_assembly/pom.xml
@@ -20,7 +20,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOT4.0.0jetty12-assembly
@@ -36,7 +36,7 @@
maven-dependency-plugin
- 3.6.1
+ 3.7.1unpack
diff --git a/lib/pom.xml b/lib/pom.xml
index 6bff51a6a..e0ece5cf9 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -22,7 +22,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTpom
diff --git a/lib/tools_api/pom.xml b/lib/tools_api/pom.xml
index baebdf60b..c45aa907c 100644
--- a/lib/tools_api/pom.xml
+++ b/lib/tools_api/pom.xml
@@ -23,7 +23,7 @@
com.google.appenginelib-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/lib/xml_validator/pom.xml b/lib/xml_validator/pom.xml
index 10861bd3f..7d1caaaa2 100644
--- a/lib/xml_validator/pom.xml
+++ b/lib/xml_validator/pom.xml
@@ -22,7 +22,7 @@
com.google.appenginelib-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjarAppEngine :: libxmlvalidator
diff --git a/lib/xml_validator_test/pom.xml b/lib/xml_validator_test/pom.xml
index 012a0f9d4..4206b8078 100644
--- a/lib/xml_validator_test/pom.xml
+++ b/lib/xml_validator_test/pom.xml
@@ -22,7 +22,7 @@
com.google.appenginelib-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjarAppEngine :: libxmlvalidator_test
diff --git a/local_runtime_shared_jetty12/pom.xml b/local_runtime_shared_jetty12/pom.xml
index ad0ffb862..0f1087b84 100644
--- a/local_runtime_shared_jetty12/pom.xml
+++ b/local_runtime_shared_jetty12/pom.xml
@@ -21,7 +21,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjarAppEngine :: appengine-local-runtime-shared Jetty12
diff --git a/local_runtime_shared_jetty9/pom.xml b/local_runtime_shared_jetty9/pom.xml
index 59538ed2d..174a3c19b 100644
--- a/local_runtime_shared_jetty9/pom.xml
+++ b/local_runtime_shared_jetty9/pom.xml
@@ -21,7 +21,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjarAppEngine :: appengine-local-runtime-shared Jetty9
diff --git a/pom.xml b/pom.xml
index a3ec42786..1023eb375 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
4.0.0com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTpomAppEngine :: Parent project
@@ -62,8 +62,10 @@
1.81.8UTF-8
- 9.4.54.v20240208
- 12.0.10
+ 9.4.55.v20240627
+ 12.0.11
+ 1.65.0
+ 4.1.111.Final2.0.13https://oss.sonatype.org/content/repositories/google-snapshots/sonatype-nexus-snapshots
@@ -208,7 +210,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 3.2.5
+ 3.3.0../deployment/target/runtime-deployment-${project.version}
@@ -234,7 +236,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 3.2.5
+ 3.3.0../deployment/target/runtime-deployment-${project.version}
@@ -415,7 +417,7 @@
org.easymockeasymock
- 5.2.0
+ 5.3.0com.google.appengine
@@ -565,7 +567,7 @@
org.apache.mavenmaven-core
- 3.9.7
+ 3.9.8org.apache.ant
@@ -581,12 +583,12 @@
org.apache.mavenmaven-plugin-api
- 3.9.7
+ 3.9.8org.checkerframeworkchecker-qual
- 3.44.0
+ 3.45.0provided
@@ -650,63 +652,63 @@
io.grpcgrpc-api
- 1.64.0
+ ${io.grpc}io.grpcgrpc-stub
- 1.64.0
+ ${io.grpc}io.grpcgrpc-protobuf
- 1.64.0
+ ${io.grpc}io.grpcgrpc-netty
- 1.64.0
+ ${io.grpc}io.nettynetty-buffer
- 4.1.110.Final
+ ${io.netty}io.nettynetty-codec
- 4.1.110.Final
+ ${io.netty}io.nettynetty-codec-http
- 4.1.110.Final
+ ${io.netty}io.nettynetty-codec-http2
- 4.1.110.Final
+ ${io.netty}io.nettynetty-common
- 4.1.110.Final
+ ${io.netty}io.nettynetty-handler
- 4.1.110.Final
+ ${io.netty}io.nettynetty-transport
- 4.1.110.Final
+ ${io.netty}io.nettynetty-transport-native-unix-common
- 4.1.110.Final
+ ${io.netty}org.apache.tomcat
@@ -716,7 +718,7 @@
com.fasterxml.jackson.corejackson-core
- 2.17.1
+ 2.17.2joda-time
@@ -743,13 +745,13 @@
com.google.truthtruth
- 1.4.2
+ 1.4.3testcom.google.truth.extensionstruth-java8-extension
- 1.4.2
+ 1.4.3test
@@ -775,7 +777,7 @@
com.google.cloudgoogle-cloud-logging
- 3.18.0
+ 3.19.0
@@ -784,7 +786,7 @@
org.codehaus.mojoversions-maven-plugin
- 2.16.2
+ 2.17.0file:///${session.executionRootDirectory}/maven-version-rules.xmlfalse
@@ -840,7 +842,7 @@
org.apache.maven.pluginsmaven-release-plugin
- 3.0.1
+ 3.1.0falsedeploy
@@ -939,7 +941,7 @@
org.codehaus.mojoversions-maven-plugin
- 2.16.2
+ 2.17.0
diff --git a/protobuf/pom.xml b/protobuf/pom.xml
index 28bcdae35..5fd6df8f6 100644
--- a/protobuf/pom.xml
+++ b/protobuf/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/quickstartgenerator/pom.xml b/quickstartgenerator/pom.xml
index cd3eab0d3..f54ebcc4a 100644
--- a/quickstartgenerator/pom.xml
+++ b/quickstartgenerator/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/quickstartgenerator_jetty12/pom.xml b/quickstartgenerator_jetty12/pom.xml
index 84c26eba0..a1f56d983 100644
--- a/quickstartgenerator_jetty12/pom.xml
+++ b/quickstartgenerator_jetty12/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/quickstartgenerator_jetty12_ee10/pom.xml b/quickstartgenerator_jetty12_ee10/pom.xml
index e88feed1c..f59f09baf 100644
--- a/quickstartgenerator_jetty12_ee10/pom.xml
+++ b/quickstartgenerator_jetty12_ee10/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/remoteapi/pom.xml b/remoteapi/pom.xml
index 5b820cedd..2141c0360 100644
--- a/remoteapi/pom.xml
+++ b/remoteapi/pom.xml
@@ -20,7 +20,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjarAppEngine :: appengine-remote-api
diff --git a/renovate.json b/renovate.json
index 72812ddaf..50a0af031 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,6 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
- "extends": ["config:recommended"],
+ "extends": ["config:recommended", "group:allNonMajor", "schedule:weekly"],
"packageRules": [
{
"matchPackageNames": [
diff --git a/runtime/annotationscanningwebapp/pom.xml b/runtime/annotationscanningwebapp/pom.xml
index ca634bd1b..05bcda902 100644
--- a/runtime/annotationscanningwebapp/pom.xml
+++ b/runtime/annotationscanningwebapp/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTcom.google.appengine.demosannotationscanningwebapp
diff --git a/runtime/deployment/pom.xml b/runtime/deployment/pom.xml
index df0d4e05d..a25137468 100644
--- a/runtime/deployment/pom.xml
+++ b/runtime/deployment/pom.xml
@@ -22,7 +22,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTpom
diff --git a/runtime/failinitfilterwebapp/pom.xml b/runtime/failinitfilterwebapp/pom.xml
index 93fb99634..eac631d82 100644
--- a/runtime/failinitfilterwebapp/pom.xml
+++ b/runtime/failinitfilterwebapp/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTcom.google.appengine.demosfailinitfilterwebapp
diff --git a/runtime/impl/pom.xml b/runtime/impl/pom.xml
index 1a7215bc3..996fa6c0f 100644
--- a/runtime/impl/pom.xml
+++ b/runtime/impl/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/AppEngineConstants.java b/runtime/impl/src/main/java/com/google/apphosting/runtime/AppEngineConstants.java
similarity index 78%
rename from runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/AppEngineConstants.java
rename to runtime/impl/src/main/java/com/google/apphosting/runtime/AppEngineConstants.java
index 3136ce11c..164ada8a2 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/AppEngineConstants.java
+++ b/runtime/impl/src/main/java/com/google/apphosting/runtime/AppEngineConstants.java
@@ -14,11 +14,29 @@
* limitations under the License.
*/
-package com.google.apphosting.runtime.jetty;
+package com.google.apphosting.runtime;
import com.google.common.collect.ImmutableSet;
+/** {@code AppEngineConstants} centralizes some constants that are specific to our use of Jetty. */
public final class AppEngineConstants {
+ /**
+ * This {@code ServletContext} attribute contains the {@link AppVersion} for the current
+ * application.
+ */
+ public static final String APP_VERSION_CONTEXT_ATTR =
+ "com.google.apphosting.runtime.jetty.APP_VERSION_CONTEXT_ATTR";
+
+ /**
+ * This {@code ServletRequest} attribute contains the {@code AppVersionKey} identifying the
+ * current application. identify which application version to use.
+ */
+ public static final String APP_VERSION_KEY_REQUEST_ATTR =
+ "com.google.apphosting.runtime.jetty.APP_VERSION_REQUEST_ATTR";
+
+ public static final String APP_YAML_ATTRIBUTE_TARGET =
+ "com.google.apphosting.runtime.jetty.appYaml";
+
/**
* The HTTP headers that are handled specially by this proxy are defined in lowercase because HTTP
* headers are case-insensitive, and we look then up in a set or switch after converting to
@@ -54,6 +72,11 @@ public final class AppEngineConstants {
public static final String X_GOOGLE_INTERNAL_PROFILER = "x-google-internal-profiler";
public static final String X_CLOUD_TRACE_CONTEXT = "x-cloud-trace-context";
+ public static final String X_APPENGINE_BACKGROUNDREQUEST = "x-appengine-backgroundrequest";
+ public static final String BACKGROUND_REQUEST_URL = "/_ah/background";
+ public static final String WARMUP_REQUEST_URL = "/_ah/start";
+ public static final String BACKGROUND_REQUEST_SOURCE_IP = "0.1.0.3";
+
public static final ImmutableSet PRIVATE_APPENGINE_HEADERS =
ImmutableSet.of(
X_APPENGINE_API_TICKET,
@@ -91,4 +114,8 @@ public final class AppEngineConstants {
public static final String ENVIRONMENT_ATTR = "appengine.environment";
public static final String HTTP_CONNECTOR_MODE = "appengine.use.HttpConnector";
+
+ public static final String IGNORE_RESPONSE_SIZE_LIMIT = "appengine.ignore.responseSizeLimit";
+
+ private AppEngineConstants() {}
}
diff --git a/runtime/impl/src/main/java/com/google/apphosting/runtime/JettyConstants.java b/runtime/impl/src/main/java/com/google/apphosting/runtime/JettyConstants.java
deleted file mode 100644
index 7896f32d5..000000000
--- a/runtime/impl/src/main/java/com/google/apphosting/runtime/JettyConstants.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.apphosting.runtime;
-
-/** {@code JettyConstants} centralizes some constants that are specific to our use of Jetty. */
-public final class JettyConstants {
- /**
- * This {@link ServletContext} attribute contains the {@link
- * AppVersion} for the current application.
- */
- public static final String APP_VERSION_CONTEXT_ATTR =
- "com.google.apphosting.runtime.jetty.APP_VERSION_CONTEXT_ATTR";
-
- /**
- * This {@code ServletRequest} attribute contains the {@link
- * AppVersionKey} identifying the current application. identify
- * which application version to use.
- */
- public static final String APP_VERSION_KEY_REQUEST_ATTR =
- "com.google.apphosting.runtime.jetty.APP_VERSION_REQUEST_ATTR";
-
- public static final String APP_YAML_ATTRIBUTE_TARGET =
- "com.google.apphosting.runtime.jetty.appYaml";
-
- private JettyConstants() {}
-}
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/LocalRpcContext.java b/runtime/impl/src/main/java/com/google/apphosting/runtime/LocalRpcContext.java
similarity index 98%
rename from runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/LocalRpcContext.java
rename to runtime/impl/src/main/java/com/google/apphosting/runtime/LocalRpcContext.java
index 163f80f58..0f65a63d2 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/LocalRpcContext.java
+++ b/runtime/impl/src/main/java/com/google/apphosting/runtime/LocalRpcContext.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.apphosting.runtime.jetty.http;
+package com.google.apphosting.runtime;
import com.google.apphosting.runtime.anyrpc.AnyRpcServerContext;
import com.google.common.util.concurrent.SettableFuture;
diff --git a/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestAPIData.java b/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestAPIData.java
index 7fad8ef29..8ede54464 100644
--- a/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestAPIData.java
+++ b/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestAPIData.java
@@ -84,4 +84,6 @@ public interface RequestAPIData {
String getUrl();
RuntimePb.UPRequest.RequestType getRequestType();
+
+ String getBackgroundRequestId();
}
diff --git a/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestRunner.java b/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestRunner.java
index 7eff965d1..f659142cf 100644
--- a/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestRunner.java
+++ b/runtime/impl/src/main/java/com/google/apphosting/runtime/RequestRunner.java
@@ -18,6 +18,7 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import com.google.appengine.api.ThreadManager;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.base.protos.HttpPb.ParsedHttpHeader;
import com.google.apphosting.base.protos.RuntimePb.UPRequest;
@@ -31,6 +32,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeoutException;
@@ -152,7 +154,8 @@ public static boolean shouldKillCloneAfterException(Throwable th) {
private String getBackgroundRequestId(UPRequest upRequest) {
for (ParsedHttpHeader header : upRequest.getRequest().getHeadersList()) {
- if (Ascii.equalsIgnoreCase(header.getKey(), "X-AppEngine-BackgroundRequest")) {
+ if (Ascii.equalsIgnoreCase(
+ header.getKey(), AppEngineConstants.X_APPENGINE_BACKGROUNDREQUEST)) {
return header.getValue();
}
}
@@ -235,22 +238,62 @@ private void dispatchRequest(RequestManager.RequestToken requestToken) throws Ex
private void dispatchBackgroundRequest() throws InterruptedException, TimeoutException {
String requestId = getBackgroundRequestId(upRequest);
- // Wait here for synchronization with the ThreadFactory.
- CountDownLatch latch = ThreadGroupPool.resetCurrentThread();
- Thread thread = new ThreadProxy();
- Runnable runnable =
- coordinator.waitForUserRunnable(
- requestId, thread, WAIT_FOR_USER_RUNNABLE_DEADLINE.toMillis());
- // Wait here until someone calls start() on the thread again.
- latch.await();
- // Now set the context class loader to the UserClassLoader for the application
- // and pass control to the Runnable the user provided.
- ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(appVersion.getClassLoader());
- try {
- runnable.run();
- } finally {
- Thread.currentThread().setContextClassLoader(oldClassLoader);
+ // For java21 runtime, RPC path, do the new background thread handling for now, and keep it for
+ // other runtimes.
+ if (!Objects.equals(System.getenv("GAE_RUNTIME"), "java21")) {
+ // Wait here for synchronization with the ThreadFactory.
+ CountDownLatch latch = ThreadGroupPool.resetCurrentThread();
+ Thread thread = new ThreadProxy();
+ Runnable runnable =
+ coordinator.waitForUserRunnable(
+ requestId, thread, WAIT_FOR_USER_RUNNABLE_DEADLINE.toMillis());
+ // Wait here until someone calls start() on the thread again.
+ latch.await();
+ // Now set the context class loader to the UserClassLoader for the application
+ // and pass control to the Runnable the user provided.
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(appVersion.getClassLoader());
+ try {
+ runnable.run();
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ }
+ } else {
+ // The interface of coordinator.waitForUserRunnable() requires us to provide the app code with
+ // a
+ // working thread *in the same exchange* where we get the runnable the user wants to run in
+ // the
+ // thread. This prevents us from actually directly feeding that runnable to the thread. To
+ // work
+ // around this conundrum, we create an EagerRunner, which lets us start running the thread
+ // without knowing yet what we want to run.
+
+ // Create an ordinary request thread as a child of this background thread.
+ EagerRunner eagerRunner = new EagerRunner();
+ Thread thread = ThreadManager.createThreadForCurrentRequest(eagerRunner);
+
+ // Give this thread to the app code and get its desired runnable in response:
+ Runnable runnable =
+ coordinator.waitForUserRunnable(
+ requestId, thread, WAIT_FOR_USER_RUNNABLE_DEADLINE.toMillis());
+
+ // Finally, hand that runnable to the thread so it can actually start working.
+ // This will block until Thread.start() is called by the app code. This is by design: we must
+ // not exit this request handler until the thread has started *and* completed, otherwise the
+ // serving infrastructure will cancel our ability to make API calls. We're effectively
+ // "holding
+ // open the door" on the spawned thread's ability to make App Engine API calls.
+ // Now set the context class loader to the UserClassLoader for the application
+ // and pass control to the Runnable the user provided.
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(appVersion.getClassLoader());
+ try {
+ eagerRunner.supplyRunnable(runnable);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ }
+ // Wait for the thread to end:
+ thread.join();
}
upResponse.setError(UPResponse.ERROR.OK_VALUE);
if (!upResponse.hasHttpResponse()) {
diff --git a/runtime/impl/src/main/java/com/google/apphosting/runtime/UpRequestAPIData.java b/runtime/impl/src/main/java/com/google/apphosting/runtime/UpRequestAPIData.java
index 9e80c17ab..df688bebd 100644
--- a/runtime/impl/src/main/java/com/google/apphosting/runtime/UpRequestAPIData.java
+++ b/runtime/impl/src/main/java/com/google/apphosting/runtime/UpRequestAPIData.java
@@ -19,7 +19,9 @@
import com.google.apphosting.base.protos.HttpPb;
import com.google.apphosting.base.protos.RuntimePb;
import com.google.apphosting.base.protos.TracePb;
+import com.google.common.base.Ascii;
import java.util.stream.Stream;
+import javax.annotation.Nullable;
public class UpRequestAPIData implements RequestAPIData {
@@ -178,4 +180,16 @@ public String getUrl() {
public RuntimePb.UPRequest.RequestType getRequestType() {
return request.getRequestType();
}
+
+ @Override
+ @Nullable
+ public String getBackgroundRequestId() {
+ for (HttpPb.ParsedHttpHeader header : request.getRequest().getHeadersList()) {
+ if (Ascii.equalsIgnoreCase(
+ header.getKey(), AppEngineConstants.X_APPENGINE_BACKGROUNDREQUEST)) {
+ return header.getValue();
+ }
+ }
+ return null;
+ }
}
diff --git a/runtime/lite/pom.xml b/runtime/lite/pom.xml
index 4fc592395..6decabb1f 100644
--- a/runtime/lite/pom.xml
+++ b/runtime/lite/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
@@ -246,7 +246,7 @@
org.apache.maven.pluginsmaven-jar-plugin
- 3.4.1
+ 3.4.2
diff --git a/runtime/lite/src/main/java/com/google/appengine/runtime/lite/BackgroundRequestDispatcher.java b/runtime/lite/src/main/java/com/google/appengine/runtime/lite/BackgroundRequestDispatcher.java
index a8e651b78..f3bb6cb28 100644
--- a/runtime/lite/src/main/java/com/google/appengine/runtime/lite/BackgroundRequestDispatcher.java
+++ b/runtime/lite/src/main/java/com/google/appengine/runtime/lite/BackgroundRequestDispatcher.java
@@ -16,6 +16,10 @@
package com.google.appengine.runtime.lite;
+import static com.google.apphosting.runtime.AppEngineConstants.BACKGROUND_REQUEST_SOURCE_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.BACKGROUND_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_BACKGROUNDREQUEST;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IP;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.appengine.api.ThreadManager;
@@ -47,11 +51,6 @@ class BackgroundRequestDispatcher extends BackgroundRequestCoordinator {
*/
private static final Duration WAIT_FOR_USER_RUNNABLE_DEADLINE = Duration.ofSeconds(60);
- private static final String X_APPENGINE_USER_IP = "x-appengine-user-ip";
- private static final String X_APPENGINE_BACKGROUNDREQUEST = "x-appengine-backgroundrequest";
- private static final String BACKGROUND_REQUEST_URL = "/_ah/background";
- private static final String BACKGROUND_REQUEST_SOURCE_IP = "0.1.0.3";
-
public AbstractHandler createHandler() {
return new BackgroundRequestHandler();
}
diff --git a/runtime/local_jetty12/pom.xml b/runtime/local_jetty12/pom.xml
index 864d865bf..4c1addf0c 100644
--- a/runtime/local_jetty12/pom.xml
+++ b/runtime/local_jetty12/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/runtime/local_jetty12_ee10/pom.xml b/runtime/local_jetty12_ee10/pom.xml
index e66d220ab..957ca08a2 100644
--- a/runtime/local_jetty12_ee10/pom.xml
+++ b/runtime/local_jetty12_ee10/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/runtime/local_jetty12_ee10/src/main/java/com/google/appengine/tools/development/jetty/ee10/DevAppEngineWebAppContext.java b/runtime/local_jetty12_ee10/src/main/java/com/google/appengine/tools/development/jetty/ee10/DevAppEngineWebAppContext.java
index d8c7a2c92..e30812d5c 100644
--- a/runtime/local_jetty12_ee10/src/main/java/com/google/appengine/tools/development/jetty/ee10/DevAppEngineWebAppContext.java
+++ b/runtime/local_jetty12_ee10/src/main/java/com/google/appengine/tools/development/jetty/ee10/DevAppEngineWebAppContext.java
@@ -23,6 +23,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.logging.Logger;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
@@ -197,9 +198,8 @@ private void disableTransportGuarantee() {
mappings.add(mapping);
}
- // TODO: do we need to call this with a new list or is modifying the ConstraintMapping
- // enough?
- securityHandler.setConstraintMappings(mappings);
+ Set knownRoles = Set.copyOf(securityHandler.getKnownRoles());
+ securityHandler.setConstraintMappings(mappings, knownRoles);
}
transportGuaranteesDisabled = true;
}
diff --git a/runtime/local_jetty9/pom.xml b/runtime/local_jetty9/pom.xml
index 735783f4d..506742d17 100644
--- a/runtime/local_jetty9/pom.xml
+++ b/runtime/local_jetty9/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/runtime/main/pom.xml b/runtime/main/pom.xml
index e520e6472..3da769847 100644
--- a/runtime/main/pom.xml
+++ b/runtime/main/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/runtime/nogaeapiswebapp/pom.xml b/runtime/nogaeapiswebapp/pom.xml
index 4e9766cdb..dec193dc2 100644
--- a/runtime/nogaeapiswebapp/pom.xml
+++ b/runtime/nogaeapiswebapp/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTcom.google.appengine.demosnogaeapiswebapp
diff --git a/runtime/pom.xml b/runtime/pom.xml
index b23e2a77e..ee3dfde20 100644
--- a/runtime/pom.xml
+++ b/runtime/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTAppEngine :: runtime projectspom
diff --git a/runtime/runtime_impl_jetty12/pom.xml b/runtime/runtime_impl_jetty12/pom.xml
index ea9b0665f..58d610791 100644
--- a/runtime/runtime_impl_jetty12/pom.xml
+++ b/runtime/runtime_impl_jetty12/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
@@ -594,7 +594,7 @@
org.apache.maven.pluginsmaven-jar-plugin
- 3.4.1
+ 3.4.2
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/JettyServletEngineAdapter.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/JettyServletEngineAdapter.java
index efd995279..a71a85a2a 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/JettyServletEngineAdapter.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/JettyServletEngineAdapter.java
@@ -15,25 +15,35 @@
*/
package com.google.apphosting.runtime.jetty;
+import static com.google.apphosting.runtime.AppEngineConstants.HTTP_CONNECTOR_MODE;
+import static com.google.apphosting.runtime.AppEngineConstants.IGNORE_RESPONSE_SIZE_LIMIT;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.base.AppVersionKey;
import com.google.apphosting.base.protos.AppinfoPb;
import com.google.apphosting.base.protos.EmptyMessage;
import com.google.apphosting.base.protos.RuntimePb.UPRequest;
import com.google.apphosting.base.protos.RuntimePb.UPResponse;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
+import com.google.apphosting.runtime.LocalRpcContext;
import com.google.apphosting.runtime.MutableUpResponse;
import com.google.apphosting.runtime.ServletEngineAdapter;
import com.google.apphosting.runtime.anyrpc.EvaluationRuntimeServerInterface;
import com.google.apphosting.runtime.jetty.delegate.DelegateConnector;
import com.google.apphosting.runtime.jetty.delegate.impl.DelegateRpcExchange;
import com.google.apphosting.runtime.jetty.http.JettyHttpHandler;
-import com.google.apphosting.runtime.jetty.http.LocalRpcContext;
import com.google.apphosting.runtime.jetty.proxy.JettyHttpProxy;
import com.google.apphosting.utils.config.AppEngineConfigException;
import com.google.apphosting.utils.config.AppYaml;
import com.google.common.flogger.GoogleLogger;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.server.HttpConfiguration;
@@ -44,19 +54,8 @@
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
-import java.util.Objects;
-import java.util.concurrent.ExecutionException;
-
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.HTTP_CONNECTOR_MODE;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
/**
* This is an implementation of ServletEngineAdapter that uses the third-party Jetty servlet engine.
- *
*/
public class JettyServletEngineAdapter implements ServletEngineAdapter {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
@@ -94,7 +93,8 @@ private static AppYaml getAppYaml(ServletEngineAdapter.Config runtimeOptions) {
try {
appYaml = AppYaml.parse(new InputStreamReader(new FileInputStream(appYamlFile), UTF_8));
} catch (FileNotFoundException | AppEngineConfigException e) {
- logger.atWarning().log("Failed to load app.yaml file at location %s - %s",
+ logger.atWarning().log(
+ "Failed to load app.yaml file at location %s - %s",
appYamlFile.getPath(), e.getMessage());
}
return appYaml;
@@ -121,15 +121,13 @@ public InvocationType getInvocationType() {
@Override
public Resource getDefaultStyleSheet() {
// TODO: this is a workaround for https://github.com/jetty/jetty.project/issues/11873
- return ResourceFactory.of(this)
- .newResource("/org/eclipse/jetty/server/jetty-dir.css");
+ return ResourceFactory.of(this).newResource("/org/eclipse/jetty/server/jetty-dir.css");
}
@Override
public Resource getDefaultFavicon() {
// TODO: this is a workaround for https://github.com/jetty/jetty.project/issues/11873
- return ResourceFactory.of(this)
- .newResource("/org/eclipse/jetty/server/favicon.ico");
+ return ResourceFactory.of(this).newResource("/org/eclipse/jetty/server/favicon.ico");
}
};
rpcConnector =
@@ -143,9 +141,10 @@ public void run(Runnable runnable) {
}
};
server.addConnector(rpcConnector);
- AppVersionHandlerFactory appVersionHandlerFactory = AppVersionHandlerFactory.newInstance(server, serverInfo);
+ AppVersionHandlerFactory appVersionHandlerFactory =
+ AppVersionHandlerFactory.newInstance(server, serverInfo);
appVersionHandler = new AppVersionHandler(appVersionHandlerFactory);
- if (!"java8".equals(System.getenv("GAE_RUNTIME"))) {
+ if (!Boolean.getBoolean(IGNORE_RESPONSE_SIZE_LIMIT)) {
CoreSizeLimitHandler sizeLimitHandler = new CoreSizeLimitHandler(-1, MAX_RESPONSE_SIZE);
sizeLimitHandler.setHandler(appVersionHandler);
server.setHandler(sizeLimitHandler);
@@ -223,7 +222,8 @@ public void deleteAppVersion(AppVersion appVersion) {
/**
* Sets the {@link com.google.apphosting.runtime.SessionStoreFactory} that will be used to create
* the list of {@link com.google.apphosting.runtime.SessionStore SessionStores} to which the HTTP
- * Session will be stored, if sessions are enabled. This method must be invoked after {@link #start(String, Config)}.
+ * Session will be stored, if sessions are enabled. This method must be invoked after {@link
+ * #start(String, Config)}.
*/
@Override
public void setSessionStoreFactory(com.google.apphosting.runtime.SessionStoreFactory factory) {
@@ -242,15 +242,15 @@ public void serviceRequest(UPRequest upRequest, MutableUpResponse upResponse) th
AppVersionKey appVersionKey = AppVersionKey.fromUpRequest(upRequest);
AppVersionKey lastVersionKey = lastAppVersionKey;
if (lastVersionKey != null) {
- // We already have created the handler on the previous request, so no need to do another getHandler().
+ // We already have created the handler on the previous request, so no need to do another
+ // getHandler().
// The two AppVersionKeys must be the same as we only support one app version.
if (!Objects.equals(appVersionKey, lastVersionKey)) {
upResponse.setError(UPResponse.ERROR.UNKNOWN_APP_VALUE);
upResponse.setErrorMessage("Unknown app: " + appVersionKey);
return;
}
- }
- else {
+ } else {
if (!appVersionHandler.ensureHandler(appVersionKey)) {
upResponse.setError(UPResponse.ERROR.UNKNOWN_APP_VALUE);
upResponse.setErrorMessage("Unknown app: " + appVersionKey);
@@ -269,7 +269,7 @@ public void serviceRequest(UPRequest upRequest, MutableUpResponse upResponse) th
httpConfiguration.setUriCompliance(UriCompliance.LEGACY);
}
DelegateRpcExchange rpcExchange = new DelegateRpcExchange(upRequest, upResponse);
- rpcExchange.setAttribute(JettyConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);
+ rpcExchange.setAttribute(AppEngineConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);
rpcExchange.setAttribute(AppEngineConstants.ENVIRONMENT_ATTR, ApiProxy.getCurrentEnvironment());
rpcConnector.service(rpcExchange);
try {
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/EE10AppVersionHandlerFactory.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/EE10AppVersionHandlerFactory.java
index e64f98f35..aa0a6684c 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/EE10AppVersionHandlerFactory.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/EE10AppVersionHandlerFactory.java
@@ -15,11 +15,12 @@
*/
package com.google.apphosting.runtime.jetty.ee10;
+import static com.google.apphosting.runtime.AppEngineConstants.HTTP_CONNECTOR_MODE;
+
import com.google.apphosting.api.ApiProxy;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
import com.google.apphosting.runtime.SessionsConfig;
-import com.google.apphosting.runtime.jetty.AppEngineConstants;
import com.google.apphosting.runtime.jetty.AppVersionHandlerFactory;
import com.google.apphosting.runtime.jetty.EE10SessionManagerHandler;
import com.google.common.flogger.GoogleLogger;
@@ -29,6 +30,10 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.jsp.JspFactory;
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.ee10.servlet.Dispatcher;
@@ -48,13 +53,6 @@
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Callback;
-import javax.servlet.jsp.JspFactory;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.HTTP_CONNECTOR_MODE;
-
/**
* {@code AppVersionHandlerFactory} implements a {@code Handler} for a given {@code AppVersionKey}.
*/
@@ -64,40 +62,48 @@ public class EE10AppVersionHandlerFactory implements AppVersionHandlerFactory {
"org.apache.tomcat.SimpleInstanceManager";
private static final String TOMCAT_INSTANCE_MANAGER = "org.apache.tomcat.InstanceManager";
private static final String TOMCAT_JSP_FACTORY = "org.apache.jasper.runtime.JspFactoryImpl";
+
/**
* Any settings in this webdefault.xml file will be inherited by all applications. We don't want
* to use Jetty's built-in webdefault.xml because we want to disable some of their functionality,
* and because we want to be explicit about what functionality we are supporting.
*/
public static final String WEB_DEFAULTS_XML =
- "com/google/apphosting/runtime/jetty/webdefault.xml";
+ "com/google/apphosting/runtime/jetty/webdefault.xml";
+
/**
* This property will be used to enable/disable Annotation Scanning when quickstart-web.xml is not
* present.
*/
private static final String USE_ANNOTATION_SCANNING = "use.annotationscanning";
+
/**
* A "private" request attribute to indicate if the dispatch to a most recent error page has run
* to completion. Note an error page itself may generate errors.
*/
static final String ERROR_PAGE_HANDLED = ErrorHandler.ERROR_PAGE + ".handled";
+
private final Server server;
private final String serverInfo;
private final boolean useJettyErrorPageHandler;
+
public EE10AppVersionHandlerFactory(Server server, String serverInfo) {
this(server, serverInfo, false);
}
+
public EE10AppVersionHandlerFactory(
Server server, String serverInfo, boolean useJettyErrorPageHandler) {
this.server = server;
this.serverInfo = serverInfo;
this.useJettyErrorPageHandler = useJettyErrorPageHandler;
}
+
/**
* Returns the {@code Handler} that will handle requests for the specified application version.
*/
@Override
- public org.eclipse.jetty.server.Handler createHandler(AppVersion appVersion) throws ServletException {
+ public org.eclipse.jetty.server.Handler createHandler(AppVersion appVersion)
+ throws ServletException {
// Need to set thread context classloader for the duration of the scope.
ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
@@ -107,12 +113,14 @@ public org.eclipse.jetty.server.Handler createHandler(AppVersion appVersion) thr
Thread.currentThread().setContextClassLoader(oldContextClassLoader);
}
}
- private org.eclipse.jetty.server.Handler doCreateHandler(AppVersion appVersion) throws ServletException {
+
+ private org.eclipse.jetty.server.Handler doCreateHandler(AppVersion appVersion)
+ throws ServletException {
try {
File contextRoot = appVersion.getRootDirectory();
final AppEngineWebAppContext context =
new AppEngineWebAppContext(
- appVersion.getRootDirectory(), serverInfo, /* extractWar=*/ false);
+ appVersion.getRootDirectory(), serverInfo, /* extractWar= */ false);
context.setServer(server);
context.setDefaultsDescriptor(WEB_DEFAULTS_XML);
ClassLoader classLoader = appVersion.getClassLoader();
@@ -143,15 +151,13 @@ private org.eclipse.jetty.server.Handler doCreateHandler(AppVersion appVersion)
// context.removeConfiguration(new JettyWebXmlConfiguration());
if (Boolean.getBoolean(USE_ANNOTATION_SCANNING)) {
context.addConfiguration(new AnnotationConfiguration());
- }
- else {
+ } else {
context.removeConfiguration(new AnnotationConfiguration());
}
File quickstartXml = new File(contextRoot, "WEB-INF/quickstart-web.xml");
if (quickstartXml.exists()) {
context.addConfiguration(new QuickStartConfiguration());
- }
- else {
+ } else {
context.removeConfiguration(new QuickStartConfiguration());
}
// TODO: review which configurations are added by default.
@@ -174,8 +180,7 @@ private org.eclipse.jetty.server.Handler doCreateHandler(AppVersion appVersion)
klass = classLoader.loadClass(TOMCAT_JSP_FACTORY);
JspFactory jspf = (JspFactory) klass.getConstructor().newInstance();
JspFactory.setDefaultFactory(jspf);
- Class.forName(
- "org.apache.jasper.compiler.JspRuntimeContext", true, classLoader);
+ Class.forName("org.apache.jasper.compiler.JspRuntimeContext", true, classLoader);
} catch (Throwable t) {
// No big deal, there are no JSPs in the App since the jsp libraries are not inside the
// web app classloader.
@@ -191,34 +196,33 @@ private org.eclipse.jetty.server.Handler doCreateHandler(AppVersion appVersion)
.setServletContextHandler(context);
EE10SessionManagerHandler.create(builder.build());
// Pass the AppVersion on to any of our servlets (e.g. ResourceFileServlet).
- context.setAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR, appVersion);
+ context.setAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR, appVersion);
if (Boolean.getBoolean(HTTP_CONNECTOR_MODE)) {
context.addEventListener(
- new ContextHandler.ContextScopeListener() {
- @Override
- public void enterScope(Context context, Request request) {
- if (request != null) {
- ApiProxy.Environment environment =
- (ApiProxy.Environment)
- request.getAttribute(AppEngineConstants.ENVIRONMENT_ATTR);
- if (environment != null) ApiProxy.setEnvironmentForCurrentThread(environment);
+ new ContextHandler.ContextScopeListener() {
+ @Override
+ public void enterScope(Context context, Request request) {
+ if (request != null) {
+ ApiProxy.Environment environment =
+ (ApiProxy.Environment)
+ request.getAttribute(AppEngineConstants.ENVIRONMENT_ATTR);
+ if (environment != null) ApiProxy.setEnvironmentForCurrentThread(environment);
+ }
}
- }
- @Override
- public void exitScope(Context context, Request request) {
- if (request != null) {
+ @Override
+ public void exitScope(Context context, Request request) {
ApiProxy.clearEnvironmentForCurrentThread();
}
- }
- });
+ });
}
return context;
} catch (Exception ex) {
throw new ServletException(ex);
}
}
+
/**
* {@code NullErrorHandler} does nothing when an error occurs. The exception is already stored in
* an attribute of {@code request}, but we don't do any rendering of it into the response, UNLESS
@@ -233,6 +237,7 @@ public boolean handle(Request request, Response response, Callback callback) thr
// We don't want Jetty to do anything further.
return true;
}
+
/**
* Try to invoke a custom error page if a handler is available. If not, render a simple HTML
* response for {@link HttpServletResponse#sendError} calls, but do nothing for unhandled
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/ResourceFileServlet.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/ResourceFileServlet.java
index 129190741..93c4f42d9 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/ResourceFileServlet.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee10/ResourceFileServlet.java
@@ -17,7 +17,7 @@
package com.google.apphosting.runtime.jetty.ee10;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.utils.config.AppYaml;
import com.google.common.base.Ascii;
import com.google.common.flogger.GoogleLogger;
@@ -71,11 +71,11 @@ public class ResourceFileServlet extends HttpServlet {
public void init() throws ServletException {
context = getServletContext();
AppVersion appVersion =
- (AppVersion) context.getAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR);
+ (AppVersion) context.getAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR);
chandler = ServletContextHandler.getServletContextHandler(context);
AppYaml appYaml =
- (AppYaml) chandler.getServer().getAttribute(JettyConstants.APP_YAML_ATTRIBUTE_TARGET);
+ (AppYaml) chandler.getServer().getAttribute(AppEngineConstants.APP_YAML_ATTRIBUTE_TARGET);
fSender = new FileSender(appYaml);
// AFAICT, there is no real API to retrieve this information, so
// we access Jetty's internal state.
@@ -261,7 +261,7 @@ private boolean maybeServeWelcomeFile(
}
AppVersion appVersion =
- (AppVersion) getServletContext().getAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR);
+ (AppVersion) getServletContext().getAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR);
ServletHandler handler = chandler.getServletHandler();
for (String welcomeName : welcomeFiles) {
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/EE8AppVersionHandlerFactory.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/EE8AppVersionHandlerFactory.java
index 9c117306a..e3de458dd 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/EE8AppVersionHandlerFactory.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/EE8AppVersionHandlerFactory.java
@@ -16,15 +16,24 @@
package com.google.apphosting.runtime.jetty.ee8;
+import static com.google.apphosting.runtime.AppEngineConstants.HTTP_CONNECTOR_MODE;
+
import com.google.apphosting.api.ApiProxy;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
import com.google.apphosting.runtime.SessionsConfig;
-import com.google.apphosting.runtime.jetty.AppEngineConstants;
import com.google.apphosting.runtime.jetty.AppVersionHandlerFactory;
import com.google.apphosting.runtime.jetty.SessionManagerHandler;
import com.google.common.flogger.GoogleLogger;
import com.google.common.html.HtmlEscapers;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
import org.eclipse.jetty.ee8.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ee8.nested.ContextHandler;
import org.eclipse.jetty.ee8.nested.Dispatcher;
@@ -37,17 +46,6 @@
import org.eclipse.jetty.ee8.webapp.WebXmlConfiguration;
import org.eclipse.jetty.server.Server;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.jsp.JspFactory;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.HTTP_CONNECTOR_MODE;
-
/**
* {@code AppVersionHandlerFactory} implements a {@code Handler} for a given {@code AppVersionKey}.
*/
@@ -82,16 +80,12 @@ public class EE8AppVersionHandlerFactory implements AppVersionHandlerFactory {
private final String serverInfo;
private final boolean useJettyErrorPageHandler;
- public EE8AppVersionHandlerFactory(
- Server server,
- String serverInfo) {
+ public EE8AppVersionHandlerFactory(Server server, String serverInfo) {
this(server, serverInfo, false);
}
public EE8AppVersionHandlerFactory(
- Server server,
- String serverInfo,
- boolean useJettyErrorPageHandler) {
+ Server server, String serverInfo, boolean useJettyErrorPageHandler) {
this.server = server;
this.serverInfo = serverInfo;
this.useJettyErrorPageHandler = useJettyErrorPageHandler;
@@ -205,34 +199,32 @@ private org.eclipse.jetty.server.Handler doCreateHandler(AppVersion appVersion)
SessionManagerHandler.create(builder.build());
// Pass the AppVersion on to any of our servlets (e.g. ResourceFileServlet).
- context.setAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR, appVersion);
+ context.setAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR, appVersion);
if (Boolean.getBoolean(HTTP_CONNECTOR_MODE)) {
context.addEventListener(
- new ContextHandler.ContextScopeListener() {
- @Override
- public void enterScope(
- ContextHandler.APIContext context,
- org.eclipse.jetty.ee8.nested.Request request,
- Object reason) {
- if (request != null) {
- ApiProxy.Environment environment =
- (ApiProxy.Environment)
- request.getAttribute(AppEngineConstants.ENVIRONMENT_ATTR);
- if (environment != null) {
- ApiProxy.setEnvironmentForCurrentThread(environment);
+ new ContextHandler.ContextScopeListener() {
+ @Override
+ public void enterScope(
+ ContextHandler.APIContext context,
+ org.eclipse.jetty.ee8.nested.Request request,
+ Object reason) {
+ if (request != null) {
+ ApiProxy.Environment environment =
+ (ApiProxy.Environment)
+ request.getAttribute(AppEngineConstants.ENVIRONMENT_ATTR);
+ if (environment != null) {
+ ApiProxy.setEnvironmentForCurrentThread(environment);
+ }
}
}
- }
- @Override
- public void exitScope(
- ContextHandler.APIContext context, org.eclipse.jetty.ee8.nested.Request request) {
- if (request != null) {
+ @Override
+ public void exitScope(
+ ContextHandler.APIContext context, org.eclipse.jetty.ee8.nested.Request request) {
ApiProxy.clearEnvironmentForCurrentThread();
}
- }
- });
+ });
}
return context.get();
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/ResourceFileServlet.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/ResourceFileServlet.java
index 2d949dd9b..f79c6ded5 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/ResourceFileServlet.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/ee8/ResourceFileServlet.java
@@ -17,7 +17,7 @@
package com.google.apphosting.runtime.jetty.ee8;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.utils.config.AppYaml;
import com.google.common.base.Ascii;
import com.google.common.flogger.GoogleLogger;
@@ -68,11 +68,11 @@ public class ResourceFileServlet extends HttpServlet {
public void init() throws ServletException {
context = getServletContext();
AppVersion appVersion =
- (AppVersion) context.getAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR);
+ (AppVersion) context.getAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR);
chandler = ContextHandler.getContextHandler(context);
AppYaml appYaml =
- (AppYaml) chandler.getServer().getAttribute(JettyConstants.APP_YAML_ATTRIBUTE_TARGET);
+ (AppYaml) chandler.getServer().getAttribute(AppEngineConstants.APP_YAML_ATTRIBUTE_TARGET);
fSender = new FileSender(appYaml);
// AFAICT, there is no real API to retrieve this information, so
// we access Jetty's internal state.
@@ -251,7 +251,7 @@ private boolean maybeServeWelcomeFile(
}
AppVersion appVersion =
- (AppVersion) getServletContext().getAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR);
+ (AppVersion) getServletContext().getAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR);
ServletHandler handler = chandler.getChildHandlerByClass(ServletHandler.class);
MappedResource defaultEntry = handler.getHolderEntry("/");
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyHttpHandler.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyHttpHandler.java
index 64a357ce5..ddf839753 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyHttpHandler.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyHttpHandler.java
@@ -16,42 +16,34 @@
package com.google.apphosting.runtime.jetty.http;
+import static com.google.apphosting.runtime.RequestRunner.WAIT_FOR_USER_RUNNABLE_DEADLINE;
+
import com.google.appengine.api.ThreadManager;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.base.AppVersionKey;
import com.google.apphosting.base.protos.EmptyMessage;
import com.google.apphosting.base.protos.RuntimePb;
import com.google.apphosting.runtime.ApiProxyImpl;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.runtime.AppVersion;
import com.google.apphosting.runtime.BackgroundRequestCoordinator;
-import com.google.apphosting.runtime.JettyConstants;
+import com.google.apphosting.runtime.LocalRpcContext;
import com.google.apphosting.runtime.RequestManager;
-import com.google.apphosting.runtime.RequestRunner;
import com.google.apphosting.runtime.RequestRunner.EagerRunner;
import com.google.apphosting.runtime.ResponseAPIData;
import com.google.apphosting.runtime.ServletEngineAdapter;
-import com.google.apphosting.runtime.jetty.AppEngineConstants;
import com.google.apphosting.runtime.jetty.AppInfoFactory;
-import com.google.common.base.Ascii;
import com.google.common.flogger.GoogleLogger;
-import org.eclipse.jetty.http.HttpField;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.Duration;
+import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
-import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Blocker;
import org.eclipse.jetty.util.Callback;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.time.Duration;
-import java.util.Optional;
-import java.util.concurrent.TimeoutException;
-
-import static com.google.apphosting.runtime.RequestRunner.WAIT_FOR_USER_RUNNABLE_DEADLINE;
-import java.util.concurrent.Exchanger;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
/**
* This class replicates the behaviour of the {@link RequestRunner} for Requests which do not come
* through RPC. It should be added as a {@link Handler} to the Jetty {@link Server} wrapping the
@@ -90,6 +82,9 @@ public JettyHttpHandler(
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {
+ // This handler cannot be used with anything else which establishes an environment
+ // (e.g. RpcConnection).
+ assert (request.getAttribute(AppEngineConstants.ENVIRONMENT_ATTR) == null);
JettyRequestAPIData genericRequest =
new JettyRequestAPIData(request, appInfoFactory, passThroughPrivateHeaders);
JettyResponseAPIData genericResponse = new JettyResponseAPIData(response);
@@ -168,7 +163,7 @@ private boolean dispatchServletRequest(
throws Throwable {
Request jettyRequest = request.getWrappedRequest();
Response jettyResponse = response.getWrappedResponse();
- jettyRequest.setAttribute(JettyConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);
+ jettyRequest.setAttribute(AppEngineConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);
// Environment is set in a request attribute which is set/unset for async threads by
// a ContextScopeListener created inside the AppVersionHandlerFactory.
@@ -285,13 +280,10 @@ public static boolean shouldKillCloneAfterException(Throwable th) {
}
private String getBackgroundRequestId(JettyRequestAPIData upRequest) {
- Optional match =
- upRequest.getOriginalRequest().getHeaders().stream()
- .filter(h -> Ascii.equalsIgnoreCase(h.getName(), "X-AppEngine-BackgroundRequest"))
- .findFirst();
- if (match.isPresent()) {
- return match.get().getValue();
+ String backgroundRequestId = upRequest.getBackgroundRequestId();
+ if (backgroundRequestId == null) {
+ throw new IllegalArgumentException("Did not receive a background request identifier.");
}
- throw new IllegalArgumentException("Did not receive a background request identifier.");
+ return backgroundRequestId;
}
}
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyRequestAPIData.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyRequestAPIData.java
index 6c56ac139..8bc17f80e 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyRequestAPIData.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/http/JettyRequestAPIData.java
@@ -17,43 +17,45 @@
package com.google.apphosting.runtime.jetty.http;
import static com.google.apphosting.base.protos.RuntimePb.UPRequest.RequestType.OTHER;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.DEFAULT_SECRET_KEY;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.IS_ADMIN_HEADER_VALUE;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.IS_TRUSTED;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.PRIVATE_APPENGINE_HEADERS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.SKIP_ADMIN_CHECK_ATTR;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.WARMUP_IP;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_API_TICKET;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_APPSERVER_DATACENTER;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_APPSERVER_TASK_BNS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_AUTH_DOMAIN;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_DEFAULT_VERSION_HOSTNAME;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_GAIA_AUTHUSER;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_GAIA_ID;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_GAIA_SESSION;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_HTTPS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_ID_HASH;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_LOAS_PEER_USERNAME;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_QUEUENAME;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_REQUEST_LOG_ID;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_TIMEOUT_MS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_TRUSTED_IP_REQUEST;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_EMAIL;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_ID;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_IP;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_IS_ADMIN;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_ORGANIZATION;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_CLOUD_TRACE_CONTEXT;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_FORWARDED_PROTO;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_GOOGLE_INTERNAL_PROFILER;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK;
+import static com.google.apphosting.runtime.AppEngineConstants.BACKGROUND_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.DEFAULT_SECRET_KEY;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_ADMIN_HEADER_VALUE;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_TRUSTED;
+import static com.google.apphosting.runtime.AppEngineConstants.PRIVATE_APPENGINE_HEADERS;
+import static com.google.apphosting.runtime.AppEngineConstants.SKIP_ADMIN_CHECK_ATTR;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_API_TICKET;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_DATACENTER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_TASK_BNS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_AUTH_DOMAIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_BACKGROUNDREQUEST;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_DEFAULT_VERSION_HOSTNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_AUTHUSER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_SESSION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_HTTPS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_ID_HASH;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_LOAS_PEER_USERNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_QUEUENAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_REQUEST_LOG_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TIMEOUT_MS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TRUSTED_IP_REQUEST;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_EMAIL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IS_ADMIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ORGANIZATION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_CLOUD_TRACE_CONTEXT;
+import static com.google.apphosting.runtime.AppEngineConstants.X_FORWARDED_PROTO;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_PROFILER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK;
import com.google.apphosting.base.protos.HttpPb;
import com.google.apphosting.base.protos.RuntimePb;
import com.google.apphosting.base.protos.TracePb;
import com.google.apphosting.runtime.RequestAPIData;
import com.google.apphosting.runtime.TraceContextHelper;
-import com.google.apphosting.runtime.jetty.AppEngineConstants;
import com.google.apphosting.runtime.jetty.AppInfoFactory;
import com.google.common.base.Strings;
import com.google.common.flogger.GoogleLogger;
@@ -103,6 +105,7 @@ public class JettyRequestAPIData implements RequestAPIData {
private String defaultVersionHostname;
private String email = "";
private String securityTicket;
+ private String backgroundRequestId;
public JettyRequestAPIData(
Request request, AppInfoFactory appInfoFactory, boolean passThroughPrivateHeaders) {
@@ -216,6 +219,10 @@ public JettyRequestAPIData(
*/
break;
+ case X_APPENGINE_BACKGROUNDREQUEST:
+ backgroundRequestId = value;
+ break;
+
default:
break;
}
@@ -237,11 +244,11 @@ public JettyRequestAPIData(
}
String decodedPath = request.getHttpURI().getDecodedPath();
- if ("/_ah/background".equals(decodedPath)) {
+ if (BACKGROUND_REQUEST_URL.equals(decodedPath)) {
if (WARMUP_IP.equals(userIp)) {
requestType = RuntimePb.UPRequest.RequestType.BACKGROUND;
}
- } else if ("/_ah/start".equals(decodedPath)) {
+ } else if (WARMUP_REQUEST_URL.equals(decodedPath)) {
if (WARMUP_IP.equals(userIp)) {
// This request came from within App Engine via secure internal channels; tell Jetty
// it's HTTPS to avoid 403 because of web.xml security-constraint checks.
@@ -310,6 +317,11 @@ public RuntimePb.UPRequest.RequestType getRequestType() {
return requestType;
}
+ @Override
+ public String getBackgroundRequestId() {
+ return backgroundRequestId;
+ }
+
@Override
public boolean hasTraceContext() {
return traceContext != null;
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/JettyHttpProxy.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/JettyHttpProxy.java
index f4fc800a1..f8f030817 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/JettyHttpProxy.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/JettyHttpProxy.java
@@ -20,12 +20,12 @@
import com.google.apphosting.base.protos.RuntimePb;
import com.google.apphosting.base.protos.RuntimePb.UPRequest;
import com.google.apphosting.base.protos.RuntimePb.UPResponse;
+import com.google.apphosting.runtime.LocalRpcContext;
import com.google.apphosting.runtime.ServletEngineAdapter;
import com.google.apphosting.runtime.anyrpc.EvaluationRuntimeServerInterface;
import com.google.apphosting.runtime.jetty.AppInfoFactory;
import com.google.apphosting.runtime.jetty.CoreSizeLimitHandler;
import com.google.apphosting.runtime.jetty.JettyServletEngineAdapter;
-import com.google.apphosting.runtime.jetty.http.LocalRpcContext;
import com.google.common.base.Ascii;
import com.google.common.base.Throwables;
import com.google.common.flogger.GoogleLogger;
@@ -85,13 +85,14 @@ public static void startServer(ServletEngineAdapter.Config runtimeOptions) {
public static ServerConnector newConnector(
Server server, ServletEngineAdapter.Config runtimeOptions) {
- ServerConnector connector = new JettyServerConnectorWithReusePort(server, runtimeOptions.jettyReusePort());
+ ServerConnector connector =
+ new JettyServerConnectorWithReusePort(server, runtimeOptions.jettyReusePort());
connector.setHost(runtimeOptions.jettyHttpAddress().getHost());
connector.setPort(runtimeOptions.jettyHttpAddress().getPort());
- HttpConfiguration config = connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
- if (JettyServletEngineAdapter.LEGACY_MODE)
- {
+ HttpConfiguration config =
+ connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
+ if (JettyServletEngineAdapter.LEGACY_MODE) {
config.setHttpCompliance(HttpCompliance.RFC7230_LEGACY);
config.setRequestCookieCompliance(CookieCompliance.RFC2965);
config.setResponseCookieCompliance(CookieCompliance.RFC2965);
@@ -215,6 +216,5 @@ private static Level toJavaLevel(long level) {
}
}
- private JettyHttpProxy() {
- }
+ private JettyHttpProxy() {}
}
diff --git a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/UPRequestTranslator.java b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/UPRequestTranslator.java
index ee2d5d66e..02c49757a 100644
--- a/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/UPRequestTranslator.java
+++ b/runtime/runtime_impl_jetty12/src/main/java/com/google/apphosting/runtime/jetty/proxy/UPRequestTranslator.java
@@ -16,36 +16,38 @@
package com.google.apphosting.runtime.jetty.proxy;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.DEFAULT_SECRET_KEY;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.IS_ADMIN_HEADER_VALUE;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.IS_TRUSTED;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.PRIVATE_APPENGINE_HEADERS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.WARMUP_IP;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_API_TICKET;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_APPSERVER_DATACENTER;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_APPSERVER_TASK_BNS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_AUTH_DOMAIN;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_DEFAULT_VERSION_HOSTNAME;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_GAIA_AUTHUSER;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_GAIA_ID;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_GAIA_SESSION;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_HTTPS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_LOAS_PEER_USERNAME;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_QUEUENAME;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_REQUEST_LOG_ID;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_TIMEOUT_MS;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_TRUSTED_IP_REQUEST;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_EMAIL;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_ID;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_IP;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_IS_ADMIN;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_NICKNAME;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_APPENGINE_USER_ORGANIZATION;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_CLOUD_TRACE_CONTEXT;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_FORWARDED_PROTO;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_GOOGLE_INTERNAL_PROFILER;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK;
-import static com.google.apphosting.runtime.jetty.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK_UC;
+import static com.google.apphosting.runtime.AppEngineConstants.BACKGROUND_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.DEFAULT_SECRET_KEY;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_ADMIN_HEADER_VALUE;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_TRUSTED;
+import static com.google.apphosting.runtime.AppEngineConstants.PRIVATE_APPENGINE_HEADERS;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_API_TICKET;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_DATACENTER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_TASK_BNS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_AUTH_DOMAIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_DEFAULT_VERSION_HOSTNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_AUTHUSER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_SESSION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_HTTPS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_LOAS_PEER_USERNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_QUEUENAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_REQUEST_LOG_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TIMEOUT_MS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TRUSTED_IP_REQUEST;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_EMAIL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IS_ADMIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_NICKNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ORGANIZATION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_CLOUD_TRACE_CONTEXT;
+import static com.google.apphosting.runtime.AppEngineConstants.X_FORWARDED_PROTO;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_PROFILER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK_UC;
import com.google.apphosting.base.protos.AppinfoPb;
import com.google.apphosting.base.protos.HttpPb;
@@ -181,11 +183,11 @@ public final RuntimePb.UPRequest translateRequest(Request jettyRequest) {
}
String decodedPath = jettyRequest.getHttpURI().getDecodedPath();
- if ("/_ah/background".equals(decodedPath)) {
+ if (BACKGROUND_REQUEST_URL.equals(decodedPath)) {
if (WARMUP_IP.equals(httpRequest.getUserIp())) {
upReqBuilder.setRequestType(UPRequest.RequestType.BACKGROUND);
}
- } else if ("/_ah/start".equals(decodedPath)) {
+ } else if (WARMUP_REQUEST_URL.equals(decodedPath)) {
if (WARMUP_IP.equals(httpRequest.getUserIp())) {
// This request came from within App Engine via secure internal channels; tell Jetty
// it's HTTPS to avoid 403 because of web.xml security-constraint checks.
diff --git a/runtime/runtime_impl_jetty9/pom.xml b/runtime/runtime_impl_jetty9/pom.xml
index 6f52c368a..19d3cb37f 100644
--- a/runtime/runtime_impl_jetty9/pom.xml
+++ b/runtime/runtime_impl_jetty9/pom.xml
@@ -23,7 +23,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
@@ -483,7 +483,7 @@
org.apache.maven.pluginsmaven-jar-plugin
- 3.4.1
+ 3.4.2
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerFactory.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerFactory.java
index b47d25a3b..db978bf11 100644
--- a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerFactory.java
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerFactory.java
@@ -16,34 +16,34 @@
package com.google.apphosting.runtime.jetty9;
-import com.google.apphosting.base.AppVersionKey;
+import com.google.apphosting.api.ApiProxy;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
import com.google.apphosting.runtime.SessionsConfig;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.GoogleLogger;
import com.google.common.html.HtmlEscapers;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
-import javax.servlet.UnavailableException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.jsp.JspFactory;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-
/**
* {@code AppVersionHandlerFactory} implements a {@code Handler} for a given {@code AppVersionKey}.
*/
@@ -141,7 +141,6 @@ private final String[] getPreconfigurationClasses() {
}
private Handler doCreateHandler(AppVersion appVersion) throws ServletException {
- AppVersionKey appVersionKey = appVersion.getKey();
try {
File contextRoot = appVersion.getRootDirectory();
@@ -198,21 +197,43 @@ private Handler doCreateHandler(AppVersion appVersion) throws ServletException {
.setAsyncPersistence(sessionsConfig.isAsyncPersistence())
.setServletContextHandler(context);
- SessionManagerHandler unused = SessionManagerHandler.create(builder.build());
+ SessionManagerHandler ignored = SessionManagerHandler.create(builder.build());
// Pass the AppVersion on to any of our servlets (e.g. ResourceFileServlet).
- context.setAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR, appVersion);
-
- context.start();
- // Check to see if servlet filter initialization failed.
- Throwable unavailableCause = context.getUnavailableException();
- if (unavailableCause != null) {
- if (unavailableCause instanceof ServletException) {
- throw (ServletException) unavailableCause;
- } else {
- UnavailableException unavailableException =
- new UnavailableException("Initialization failed.");
- unavailableException.initCause(unavailableCause);
- throw unavailableException;
+ context.setAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR, appVersion);
+ if (Boolean.getBoolean(AppEngineConstants.HTTP_CONNECTOR_MODE)) {
+ context.addEventListener(
+ new ContextHandler.ContextScopeListener() {
+ @Override
+ public void enterScope(
+ ContextHandler.Context context, Request request, Object reason) {
+ if (request != null) {
+ ApiProxy.Environment environment =
+ (ApiProxy.Environment)
+ request.getAttribute(AppEngineConstants.ENVIRONMENT_ATTR);
+ if (environment != null) {
+ ApiProxy.setEnvironmentForCurrentThread(environment);
+ }
+ }
+ }
+
+ @Override
+ public void exitScope(ContextHandler.Context context, Request request) {
+ ApiProxy.clearEnvironmentForCurrentThread();
+ }
+ });
+ } else {
+ context.start();
+ // Check to see if servlet filter initialization failed.
+ Throwable unavailableCause = context.getUnavailableException();
+ if (unavailableCause != null) {
+ if (unavailableCause instanceof ServletException) {
+ throw (ServletException) unavailableCause;
+ } else {
+ UnavailableException unavailableException =
+ new UnavailableException("Initialization failed.");
+ unavailableException.initCause(unavailableCause);
+ throw unavailableException;
+ }
}
}
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerMap.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerMap.java
index 9a2b7d191..adcbaa488 100644
--- a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerMap.java
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/AppVersionHandlerMap.java
@@ -17,22 +17,20 @@
package com.google.apphosting.runtime.jetty9;
import com.google.apphosting.base.AppVersionKey;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
-import com.google.apphosting.runtime.SessionStore;
import com.google.apphosting.runtime.SessionStoreFactory;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandlerContainer;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandlerContainer;
/**
* {@code AppVersionHandlerMap} is a {@code HandlerContainer} that identifies each child {@code
@@ -41,7 +39,6 @@
*
In order to identify which application version each request should be sent to, this class
* assumes that an attribute will be set on the {@code HttpServletRequest} with a value of the
* {@code AppVersionKey} that should be used.
- *
*/
public class AppVersionHandlerMap extends AbstractHandlerContainer {
private final AppVersionHandlerFactory appVersionHandlerFactory;
@@ -95,6 +92,10 @@ public synchronized Handler getHandler(AppVersionKey appVersionKey) throws Servl
return handler;
}
+ public AppVersion getAppVersion(AppVersionKey appVersionKey) {
+ return appVersionMap.get(appVersionKey);
+ }
+
/**
* Forward the specified request on to the {@link Handler} associated with its application
* version.
@@ -104,7 +105,7 @@ public void handle(
String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
AppVersionKey appVersionKey =
- (AppVersionKey) request.getAttribute(JettyConstants.APP_VERSION_KEY_REQUEST_ATTR);
+ (AppVersionKey) request.getAttribute(AppEngineConstants.APP_VERSION_KEY_REQUEST_ATTR);
if (appVersionKey == null) {
throw new ServletException("Request did not provide an application version");
}
@@ -127,24 +128,6 @@ public void handle(
}
}
- @Override
- protected void doStart() throws Exception {
- for (Handler handler : getHandlers()) {
- handler.start();
- }
-
- super.doStart();
- }
-
- @Override
- protected void doStop() throws Exception {
- super.doStop();
-
- for (Handler handler : getHandlers()) {
- handler.stop();
- }
- }
-
@Override
public void setServer(Server server) {
super.setServer(server);
@@ -159,29 +142,17 @@ public Handler[] getHandlers() {
return handlerMap.values().toArray(new Handler[0]);
}
- /**
- * Not supported.
- *
- * @throws UnsupportedOperationException
- */
+ /** Not supported. */
public void setHandlers(Handler[] handlers) {
throw new UnsupportedOperationException();
}
- /**
- * Not supported.
- *
- * @throws UnsupportedOperationException
- */
+ /** Not supported. */
public void addHandler(Handler handler) {
throw new UnsupportedOperationException();
}
- /**
- * Not supported.
- *
- * @throws UnsupportedOperationException
- */
+ /** Not supported. */
public void removeHandler(Handler handler) {
throw new UnsupportedOperationException();
}
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyHttpHandler.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyHttpHandler.java
new file mode 100644
index 000000000..3146c22cd
--- /dev/null
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyHttpHandler.java
@@ -0,0 +1,288 @@
+/*
+ * 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.apphosting.runtime.jetty9;
+
+import static com.google.apphosting.runtime.RequestRunner.WAIT_FOR_USER_RUNNABLE_DEADLINE;
+import static com.google.common.base.Verify.verify;
+
+import com.google.appengine.api.ThreadManager;
+import com.google.apphosting.api.ApiProxy;
+import com.google.apphosting.base.AppVersionKey;
+import com.google.apphosting.base.protos.EmptyMessage;
+import com.google.apphosting.base.protos.RuntimePb;
+import com.google.apphosting.runtime.ApiProxyImpl;
+import com.google.apphosting.runtime.AppEngineConstants;
+import com.google.apphosting.runtime.AppVersion;
+import com.google.apphosting.runtime.BackgroundRequestCoordinator;
+import com.google.apphosting.runtime.LocalRpcContext;
+import com.google.apphosting.runtime.RequestManager;
+import com.google.apphosting.runtime.RequestRunner.EagerRunner;
+import com.google.apphosting.runtime.ResponseAPIData;
+import com.google.apphosting.runtime.ServletEngineAdapter;
+import com.google.common.flogger.GoogleLogger;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.Duration;
+import java.util.concurrent.TimeoutException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+
+/**
+ * This class replicates the behaviour of the {@link RequestRunner} for Requests which do not come
+ * through RPC. It should be added as a {@link Handler} to the Jetty {@link Server} wrapping the
+ * {@code AppEngineWebAppContext}.
+ *
+ *
This uses the {@link RequestManager} to start any AppEngine state associated with this request
+ * including the {@link ApiProxy.Environment} which it sets as a request attribute at {@link
+ * AppEngineConstants#ENVIRONMENT_ATTR}. This request attribute is pulled out by {@code
+ * ContextScopeListener}s installed by the {@code AppVersionHandlerFactory} implementations so that
+ * the {@link ApiProxy.Environment} is available all threads which are used to handle the request.
+ */
+public class JettyHttpHandler extends HandlerWrapper {
+ private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
+
+ private final boolean passThroughPrivateHeaders;
+ private final AppInfoFactory appInfoFactory;
+ private final AppVersionKey appVersionKey;
+ private final AppVersion appVersion;
+ private final RequestManager requestManager;
+ private final BackgroundRequestCoordinator coordinator;
+
+ public JettyHttpHandler(
+ ServletEngineAdapter.Config runtimeOptions,
+ AppVersion appVersion,
+ AppVersionKey appVersionKey,
+ AppInfoFactory appInfoFactory) {
+ this.passThroughPrivateHeaders = runtimeOptions.passThroughPrivateHeaders();
+ this.appInfoFactory = appInfoFactory;
+ this.appVersionKey = appVersionKey;
+ this.appVersion = appVersion;
+
+ ApiProxyImpl apiProxyImpl = (ApiProxyImpl) ApiProxy.getDelegate();
+ coordinator = apiProxyImpl.getBackgroundRequestCoordinator();
+ requestManager = (RequestManager) apiProxyImpl.getRequestThreadManager();
+ }
+
+ @Override
+ public void handle(
+ String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ // This handler cannot be used with anything else which establishes an environment
+ // (e.g. RpcConnection).
+ verify(request.getAttribute(AppEngineConstants.ENVIRONMENT_ATTR) == null);
+ JettyRequestAPIData genericRequest =
+ new JettyRequestAPIData(baseRequest, request, appInfoFactory, passThroughPrivateHeaders);
+ JettyResponseAPIData genericResponse =
+ new JettyResponseAPIData(baseRequest.getResponse(), response);
+
+ // Read time remaining in request from headers and pass value to LocalRpcContext for use in
+ // reporting remaining time until deadline for API calls (see b/154745969)
+ Duration timeRemaining = genericRequest.getTimeRemaining();
+
+ ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
+ LocalRpcContext context =
+ new LocalRpcContext<>(EmptyMessage.class, timeRemaining);
+ RequestManager.RequestToken requestToken =
+ requestManager.startRequest(
+ appVersion, context, genericRequest, genericResponse, currentThreadGroup);
+
+ // Set the environment as a request attribute, so it can be pulled out and set for async
+ // threads.
+ ApiProxy.Environment currentEnvironment = ApiProxy.getCurrentEnvironment();
+ request.setAttribute(AppEngineConstants.ENVIRONMENT_ATTR, currentEnvironment);
+
+ try {
+ dispatchRequest(target, requestToken, genericRequest, genericResponse);
+ if (!baseRequest.isHandled()) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "request not handled");
+ }
+ } catch (
+ @SuppressWarnings("InterruptedExceptionSwallowed")
+ Throwable ex) {
+ // Note we do intentionally swallow InterruptException.
+ // We will report the exception via the rpc. We don't mark this thread as interrupted because
+ // ThreadGroupPool would use that as a signal to remove the thread from the pool; we don't
+ // need that.
+ handleException(ex, requestToken, genericResponse);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
+ } finally {
+ requestManager.finishRequest(requestToken);
+ }
+ // Do not put this in a final block. If we propagate an
+ // exception the callback will be invoked automatically.
+ genericResponse.finishWithResponse(context);
+ // We don't want threads used for background requests to go back
+ // in the thread pool, because users may have stashed references
+ // to them or may be expecting them to exit. Setting the
+ // interrupt bit causes the pool to drop them.
+ if (genericRequest.getRequestType() == RuntimePb.UPRequest.RequestType.BACKGROUND) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private void dispatchRequest(
+ String target,
+ RequestManager.RequestToken requestToken,
+ JettyRequestAPIData request,
+ JettyResponseAPIData response)
+ throws Throwable {
+ switch (request.getRequestType()) {
+ case SHUTDOWN:
+ logger.atInfo().log("Shutting down requests");
+ requestManager.shutdownRequests(requestToken);
+ request.getBaseRequest().setHandled(true);
+ break;
+ case BACKGROUND:
+ dispatchBackgroundRequest(request);
+ request.getBaseRequest().setHandled(true);
+ break;
+ case OTHER:
+ dispatchServletRequest(target, request, response);
+ break;
+ }
+ }
+
+ private void dispatchServletRequest(
+ String target, JettyRequestAPIData request, JettyResponseAPIData response) throws Throwable {
+ Request baseRequest = request.getBaseRequest();
+ HttpServletRequest httpServletRequest = request.getHttpServletRequest();
+ HttpServletResponse httpServletResponse = response.getHttpServletResponse();
+ baseRequest.setAttribute(AppEngineConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);
+
+ // Environment is set in a request attribute which is set/unset for async threads by
+ // a ContextScopeListener created inside the AppVersionHandlerFactory.
+ super.handle(target, baseRequest, httpServletRequest, httpServletResponse);
+ }
+
+ private void dispatchBackgroundRequest(JettyRequestAPIData request)
+ throws InterruptedException, TimeoutException {
+ String requestId = getBackgroundRequestId(request);
+ // The interface of coordinator.waitForUserRunnable() requires us to provide the app code with a
+ // working thread *in the same exchange* where we get the runnable the user wants to run in the
+ // thread. This prevents us from actually directly feeding that runnable to the thread. To work
+ // around this conundrum, we create an EagerRunner, which lets us start running the thread
+ // without knowing yet what we want to run.
+
+ // Create an ordinary request thread as a child of this background thread.
+ EagerRunner eagerRunner = new EagerRunner();
+ Thread thread = ThreadManager.createThreadForCurrentRequest(eagerRunner);
+
+ // Give this thread to the app code and get its desired runnable in response:
+ Runnable runnable =
+ coordinator.waitForUserRunnable(
+ requestId, thread, WAIT_FOR_USER_RUNNABLE_DEADLINE.toMillis());
+
+ // Finally, hand that runnable to the thread so it can actually start working.
+ // This will block until Thread.start() is called by the app code. This is by design: we must
+ // not exit this request handler until the thread has started *and* completed, otherwise the
+ // serving infrastructure will cancel our ability to make API calls. We're effectively "holding
+ // open the door" on the spawned thread's ability to make App Engine API calls.
+ // Now set the context class loader to the UserClassLoader for the application
+ // and pass control to the Runnable the user provided.
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(appVersion.getClassLoader());
+ try {
+ eagerRunner.supplyRunnable(runnable);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ }
+ // Wait for the thread to end:
+ thread.join();
+ }
+
+ private void handleException(
+ Throwable ex, RequestManager.RequestToken requestToken, ResponseAPIData response) {
+ // Unwrap ServletException, either from javax or from jakarta exception:
+ try {
+ java.lang.reflect.Method getRootCause = ex.getClass().getMethod("getRootCause");
+ Object rootCause = getRootCause.invoke(ex);
+ if (rootCause != null) {
+ ex = (Throwable) rootCause;
+ }
+ } catch (Throwable ignore) {
+ // We do want to ignore there, failure is propagated below in the protocol buffer.
+ }
+ String msg = "Uncaught exception from servlet";
+ logger.atWarning().withCause(ex).log("%s", msg);
+ // Don't use ApiProxy here, because we don't know what state the
+ // environment/delegate are in.
+ requestToken.addAppLogMessage(ApiProxy.LogRecord.Level.fatal, formatLogLine(msg, ex));
+
+ if (shouldKillCloneAfterException(ex)) {
+ logger.atSevere().log("Detected a dangerous exception, shutting down clone nicely.");
+ response.setTerminateClone(true);
+ }
+ RuntimePb.UPResponse.ERROR error = RuntimePb.UPResponse.ERROR.APP_FAILURE;
+ setFailure(response, error, "Unexpected exception from servlet: " + ex);
+ }
+
+ /** Create a failure response from the given code and message. */
+ public static void setFailure(
+ ResponseAPIData response, RuntimePb.UPResponse.ERROR error, String message) {
+ logger.atWarning().log("Runtime failed: %s, %s", error, message);
+ // If the response is already set, use that -- it's probably more
+ // specific (e.g. THREADS_STILL_RUNNING).
+ if (response.getError() == RuntimePb.UPResponse.ERROR.OK_VALUE) {
+ response.error(error.getNumber(), message);
+ }
+ }
+
+ private String formatLogLine(String message, Throwable ex) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ printWriter.println(message);
+ ex.printStackTrace(printWriter);
+ return stringWriter.toString();
+ }
+
+ public static boolean shouldKillCloneAfterException(Throwable th) {
+ while (th != null) {
+ if (th instanceof OutOfMemoryError) {
+ return true;
+ }
+ try {
+ Throwable[] suppressed = th.getSuppressed();
+ if (suppressed != null) {
+ for (Throwable s : suppressed) {
+ if (shouldKillCloneAfterException(s)) {
+ return true;
+ }
+ }
+ }
+ } catch (OutOfMemoryError ex) {
+ return true;
+ }
+ // TODO: Consider checking for other subclasses of
+ // VirtualMachineError, but probably not StackOverflowError.
+ th = th.getCause();
+ }
+ return false;
+ }
+
+ private String getBackgroundRequestId(JettyRequestAPIData upRequest) {
+ String backgroundRequestId = upRequest.getBackgroundRequestId();
+ if (backgroundRequestId == null) {
+ throw new IllegalArgumentException("Did not receive a background request identifier.");
+ }
+ return backgroundRequestId;
+ }
+}
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyHttpProxy.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyHttpProxy.java
index b25b5048d..ec75a1683 100644
--- a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyHttpProxy.java
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyHttpProxy.java
@@ -17,30 +17,23 @@
package com.google.apphosting.runtime.jetty9;
import com.google.apphosting.base.protos.AppLogsPb;
-import com.google.apphosting.base.protos.AppinfoPb;
-import com.google.apphosting.base.protos.EmptyMessage;
import com.google.apphosting.base.protos.RuntimePb;
import com.google.apphosting.base.protos.RuntimePb.UPRequest;
import com.google.apphosting.base.protos.RuntimePb.UPResponse;
+import com.google.apphosting.runtime.LocalRpcContext;
import com.google.apphosting.runtime.ServletEngineAdapter;
-import com.google.apphosting.runtime.anyrpc.AnyRpcServerContext;
import com.google.apphosting.runtime.anyrpc.EvaluationRuntimeServerInterface;
import com.google.common.base.Ascii;
import com.google.common.base.Throwables;
import com.google.common.flogger.GoogleLogger;
import com.google.common.primitives.Ints;
-import com.google.common.util.concurrent.SettableFuture;
-import com.google.protobuf.MessageLite;
-import java.io.IOException;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpCompliance;
-import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
@@ -83,7 +76,6 @@ public static void startServer(ServletEngineAdapter.Config runtimeOptions) {
System.setProperty(JETTY_LOG_CLASS, JETTY_STDERRLOG);
ForwardingHandler handler = new ForwardingHandler(runtimeOptions, System.getenv());
- handler.init();
Server server = newServer(runtimeOptions, handler);
server.start();
} catch (Exception ex) {
@@ -91,17 +83,14 @@ public static void startServer(ServletEngineAdapter.Config runtimeOptions) {
}
}
- public static Server newServer(
- ServletEngineAdapter.Config runtimeOptions, ForwardingHandler handler) {
- Server server = new Server();
-
- ServerConnector c =
+ public static ServerConnector newConnector(
+ Server server, ServletEngineAdapter.Config runtimeOptions) {
+ ServerConnector connector =
new JettyServerConnectorWithReusePort(server, runtimeOptions.jettyReusePort());
- c.setHost(runtimeOptions.jettyHttpAddress().getHost());
- c.setPort(runtimeOptions.jettyHttpAddress().getPort());
- server.setConnectors(new Connector[] {c});
+ connector.setHost(runtimeOptions.jettyHttpAddress().getHost());
+ connector.setPort(runtimeOptions.jettyHttpAddress().getPort());
- HttpConnectionFactory factory = c.getConnectionFactory(HttpConnectionFactory.class);
+ HttpConnectionFactory factory = connector.getConnectionFactory(HttpConnectionFactory.class);
factory.setHttpCompliance(
RpcConnector.LEGACY_MODE ? HttpCompliance.RFC7230_LEGACY : HttpCompliance.RFC7230);
@@ -112,72 +101,32 @@ public static Server newServer(
config.setSendServerVersion(false);
config.setSendXPoweredBy(false);
+ return connector;
+ }
+
+ public static void insertHandlers(Server server) {
SizeLimitHandler sizeLimitHandler = new SizeLimitHandler(MAX_REQUEST_SIZE, -1);
- sizeLimitHandler.setHandler(handler);
+ sizeLimitHandler.setHandler(server.getHandler());
GzipHandler gzip = new GzipHandler();
gzip.setInflateBufferSize(8 * 1024);
gzip.setHandler(sizeLimitHandler);
gzip.setExcludedAgentPatterns();
-
- // Include all methods for the GzipHandler.
- gzip.setIncludedMethods();
-
+ gzip.setIncludedMethods(); // Include all methods for the GzipHandler.
server.setHandler(gzip);
-
- logger.atInfo().log("Starting Jetty http server for Java runtime proxy.");
- return server;
}
- private static class LocalRpcContext implements AnyRpcServerContext {
- // We just dole out sequential ids here so we can tell requests apart in the logs.
- private static final AtomicLong globalIds = new AtomicLong();
-
- private final Class responseMessageClass;
- private final long startTimeMillis;
- private final Duration timeRemaining;
- private final SettableFuture futureResponse = SettableFuture.create();
- private final long globalId = globalIds.getAndIncrement();
-
- private LocalRpcContext(Class responseMessageClass) {
- this(responseMessageClass, Duration.ofNanos((long) Double.MAX_VALUE));
- }
-
- private LocalRpcContext(Class responseMessageClass, Duration timeRemaining) {
- this.responseMessageClass = responseMessageClass;
- this.startTimeMillis = System.currentTimeMillis();
- this.timeRemaining = timeRemaining;
- }
-
- @Override
- public void finishWithResponse(MessageLite response) {
- futureResponse.set(responseMessageClass.cast(response));
- }
-
- M getResponse() throws ExecutionException, InterruptedException {
- return futureResponse.get();
- }
-
- @Override
- public void finishWithAppError(int appErrorCode, String errorDetail) {
- String message = "AppError: code " + appErrorCode + "; errorDetail " + errorDetail;
- futureResponse.setException(new RuntimeException(message));
- }
-
- @Override
- public Duration getTimeRemaining() {
- return timeRemaining;
- }
+ public static Server newServer(
+ ServletEngineAdapter.Config runtimeOptions, ForwardingHandler handler) {
+ Server server = new Server();
+ server.setHandler(handler);
+ insertHandlers(server);
- @Override
- public long getGlobalId() {
- return globalId;
- }
+ ServerConnector connector = newConnector(server, runtimeOptions);
+ server.addConnector(connector);
- @Override
- public long getStartTimeMillis() {
- return startTimeMillis;
- }
+ logger.atInfo().log("Starting Jetty http server for Java runtime proxy.");
+ return server;
}
/**
@@ -189,37 +138,17 @@ public static class ForwardingHandler extends AbstractHandler {
private static final String X_APPENGINE_TIMEOUT_MS = "x-appengine-timeout-ms";
- private final String applicationRoot;
- private final String fixedApplicationPath;
- private final AppInfoFactory appInfoFactory;
private final EvaluationRuntimeServerInterface evaluationRuntimeServerInterface;
private final UPRequestTranslator upRequestTranslator;
- public ForwardingHandler(ServletEngineAdapter.Config runtimeOptions, Map env)
- throws ExecutionException, InterruptedException, IOException {
- this.applicationRoot = runtimeOptions.applicationRoot();
- this.fixedApplicationPath = runtimeOptions.fixedApplicationPath();
- this.appInfoFactory = new AppInfoFactory(env);
+ public ForwardingHandler(ServletEngineAdapter.Config runtimeOptions, Map env) {
+ AppInfoFactory appInfoFactory = new AppInfoFactory(env);
this.evaluationRuntimeServerInterface = runtimeOptions.evaluationRuntimeServerInterface();
this.upRequestTranslator =
new UPRequestTranslator(
- this.appInfoFactory,
+ appInfoFactory,
runtimeOptions.passThroughPrivateHeaders(),
- /*skipPostData=*/ false);
- }
-
- private void init() {
- /* The init actions are not done in the constructor as they are not used when testing */
- try {
- AppinfoPb.AppInfo appinfo =
- appInfoFactory.getAppInfoFromFile(applicationRoot, fixedApplicationPath);
- // TODO Should we also call ApplyCloneSettings()?
- LocalRpcContext context = new LocalRpcContext<>(EmptyMessage.class);
- evaluationRuntimeServerInterface.addAppVersion(context, appinfo);
- Object unused = context.getResponse();
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
+ /* skipPostData= */ false);
}
/**
@@ -289,6 +218,5 @@ private static Level toJavaLevel(long level) {
}
}
- private JettyHttpProxy() {
- }
+ private JettyHttpProxy() {}
}
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyRequestAPIData.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyRequestAPIData.java
new file mode 100644
index 000000000..43fd0e75d
--- /dev/null
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyRequestAPIData.java
@@ -0,0 +1,503 @@
+/*
+ * 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.apphosting.runtime.jetty9;
+
+import static com.google.apphosting.base.protos.RuntimePb.UPRequest.RequestType.OTHER;
+import static com.google.apphosting.runtime.AppEngineConstants.BACKGROUND_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.DEFAULT_SECRET_KEY;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_ADMIN_HEADER_VALUE;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_TRUSTED;
+import static com.google.apphosting.runtime.AppEngineConstants.PRIVATE_APPENGINE_HEADERS;
+import static com.google.apphosting.runtime.AppEngineConstants.SKIP_ADMIN_CHECK_ATTR;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_API_TICKET;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_DATACENTER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_TASK_BNS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_AUTH_DOMAIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_BACKGROUNDREQUEST;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_DEFAULT_VERSION_HOSTNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_AUTHUSER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_SESSION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_HTTPS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_ID_HASH;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_LOAS_PEER_USERNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_QUEUENAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_REQUEST_LOG_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TIMEOUT_MS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TRUSTED_IP_REQUEST;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_EMAIL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IS_ADMIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ORGANIZATION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_CLOUD_TRACE_CONTEXT;
+import static com.google.apphosting.runtime.AppEngineConstants.X_FORWARDED_PROTO;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_PROFILER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK;
+
+import com.google.apphosting.base.protos.HttpPb;
+import com.google.apphosting.base.protos.RuntimePb;
+import com.google.apphosting.base.protos.TracePb;
+import com.google.apphosting.base.protos.TracePb.TraceContextProto;
+import com.google.apphosting.runtime.RequestAPIData;
+import com.google.apphosting.runtime.TraceContextHelper;
+import com.google.common.base.Strings;
+import com.google.common.flogger.GoogleLogger;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.Stream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.server.Request;
+
+/**
+ * Implementation for the {@link RequestAPIData} to allow for the Jetty {@link Request} to be used
+ * directly with the Java Runtime without any conversion into the RPC {@link RuntimePb.UPRequest}.
+ *
+ *
This will interpret the AppEngine specific headers defined in {@link AppEngineConstants}. The
+ * request returned by {@link #getBaseRequest()} is to be passed to the application and will hide
+ * any private appengine headers from {@link AppEngineConstants#PRIVATE_APPENGINE_HEADERS}.
+ */
+public class JettyRequestAPIData implements RequestAPIData {
+ private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
+
+ private final Request baseRequest;
+ private final HttpServletRequest httpServletRequest;
+ private final AppInfoFactory appInfoFactory;
+ private final String url;
+ private Duration duration = Duration.ofNanos(Long.MAX_VALUE);
+ private RuntimePb.UPRequest.RequestType requestType = OTHER;
+ private String authDomain = "";
+ private boolean isTrusted;
+ private boolean isTrustedApp;
+ private boolean isAdmin;
+ private boolean isHttps;
+ private boolean isOffline;
+ private TracePb.TraceContextProto traceContext;
+ private String obfuscatedGaiaId;
+ private String userOrganization = "";
+ private String peerUsername;
+ private long gaiaId;
+ private String authUser;
+ private String gaiaSession;
+ private String appserverDataCenter;
+ String appserverTaskBns;
+ String eventIdHash;
+ private String requestLogId;
+ private String defaultVersionHostname;
+ private String email = "";
+ private String securityTicket;
+ private String backgroundRequestId;
+
+ public JettyRequestAPIData(
+ Request request,
+ HttpServletRequest httpServletRequest,
+ AppInfoFactory appInfoFactory,
+ boolean passThroughPrivateHeaders) {
+ this.appInfoFactory = appInfoFactory;
+
+ // Can be overridden by X_APPENGINE_USER_IP header.
+ String userIp = request.getRemoteAddr();
+
+ // Can be overridden by X_APPENGINE_API_TICKET header.
+ this.securityTicket = DEFAULT_SECRET_KEY;
+
+ HttpFields fields = new HttpFields();
+ List headerNames = Collections.list(request.getHeaderNames());
+ for (String headerName : headerNames) {
+ String name = headerName.toLowerCase(Locale.ROOT);
+ String value = request.getHeader(headerName);
+ if (Strings.isNullOrEmpty(value)) {
+ continue;
+ }
+
+ switch (name) {
+ case X_APPENGINE_TRUSTED_IP_REQUEST:
+ // If there is a value, then the application is trusted
+ // If the value is IS_TRUSTED, then the user is trusted
+ isTrusted = value.equals(IS_TRUSTED);
+ isTrustedApp = true;
+ break;
+ case X_APPENGINE_HTTPS:
+ isHttps = value.equals("on");
+ break;
+ case X_APPENGINE_USER_IP:
+ userIp = value;
+ break;
+ case X_FORWARDED_PROTO:
+ isHttps = value.equals("https");
+ break;
+ case X_APPENGINE_USER_ID:
+ obfuscatedGaiaId = value;
+ break;
+ case X_APPENGINE_USER_ORGANIZATION:
+ userOrganization = value;
+ break;
+ case X_APPENGINE_LOAS_PEER_USERNAME:
+ peerUsername = value;
+ break;
+ case X_APPENGINE_GAIA_ID:
+ gaiaId = Long.parseLong(value);
+ break;
+ case X_APPENGINE_GAIA_AUTHUSER:
+ authUser = value;
+ break;
+ case X_APPENGINE_GAIA_SESSION:
+ gaiaSession = value;
+ break;
+ case X_APPENGINE_APPSERVER_DATACENTER:
+ appserverDataCenter = value;
+ break;
+ case X_APPENGINE_APPSERVER_TASK_BNS:
+ appserverTaskBns = value;
+ break;
+ case X_APPENGINE_ID_HASH:
+ eventIdHash = value;
+ break;
+ case X_APPENGINE_REQUEST_LOG_ID:
+ requestLogId = value;
+ break;
+ case X_APPENGINE_DEFAULT_VERSION_HOSTNAME:
+ defaultVersionHostname = value;
+ break;
+ case X_APPENGINE_USER_IS_ADMIN:
+ isAdmin = Objects.equals(value, IS_ADMIN_HEADER_VALUE);
+ break;
+ case X_APPENGINE_USER_EMAIL:
+ email = value;
+ break;
+ case X_APPENGINE_AUTH_DOMAIN:
+ authDomain = value;
+ break;
+ case X_APPENGINE_API_TICKET:
+ securityTicket = value;
+ break;
+
+ case X_CLOUD_TRACE_CONTEXT:
+ try {
+ traceContext = TraceContextHelper.parseTraceContextHeader(value);
+ } catch (NumberFormatException e) {
+ logger.atWarning().withCause(e).log("Could not parse trace context header: %s", value);
+ }
+ break;
+
+ case X_GOOGLE_INTERNAL_SKIPADMINCHECK:
+ request.setAttribute(SKIP_ADMIN_CHECK_ATTR, true);
+ isHttps = true;
+ break;
+
+ case X_APPENGINE_QUEUENAME:
+ request.setAttribute(SKIP_ADMIN_CHECK_ATTR, true);
+ isOffline = true;
+ break;
+
+ case X_APPENGINE_TIMEOUT_MS:
+ duration = Duration.ofMillis(Long.parseLong(value));
+ break;
+
+ case X_GOOGLE_INTERNAL_PROFILER:
+ /* TODO: what to do here?
+ try {
+ TextFormat.merge(value, upReqBuilder.getProfilerSettingsBuilder());
+ } catch (IOException ex) {
+ throw new IllegalStateException("X-Google-Internal-Profiler read content error:", ex);
+ }
+ */
+ break;
+
+ case X_APPENGINE_BACKGROUNDREQUEST:
+ backgroundRequestId = value;
+ break;
+
+ default:
+ break;
+ }
+
+ if (passThroughPrivateHeaders || !PRIVATE_APPENGINE_HEADERS.contains(name)) {
+ // Only non AppEngine specific headers are passed to the application.
+ fields.add(name, value);
+ }
+ }
+
+ HttpURI httpUri;
+ boolean isSecure;
+ if (isHttps) {
+ httpUri = new HttpURI(request.getHttpURI());
+ httpUri.setScheme(HttpScheme.HTTPS.asString());
+ isSecure = true;
+ } else {
+ httpUri = request.getHttpURI();
+ isSecure = request.isSecure();
+ }
+
+ String decodedPath = request.getHttpURI().getDecodedPath();
+ if (Objects.equals(decodedPath, BACKGROUND_REQUEST_URL)) {
+ if (Objects.equals(userIp, WARMUP_IP)) {
+ requestType = RuntimePb.UPRequest.RequestType.BACKGROUND;
+ }
+ } else if (Objects.equals(decodedPath, WARMUP_REQUEST_URL)) {
+ if (Objects.equals(userIp, WARMUP_IP)) {
+ // This request came from within App Engine via secure internal channels; tell Jetty
+ // it's HTTPS to avoid 403 because of web.xml security-constraint checks.
+ isHttps = true;
+ }
+ }
+
+ HttpURI uri = new HttpURI(httpUri);
+ uri.setQuery(null);
+ StringBuilder sb = new StringBuilder(uri.toString());
+ String query = httpUri.getQuery();
+ // No need to escape, URL retains any %-escaping it might have, which is what we want.
+ if (query != null) {
+ sb.append('?').append(query);
+ }
+ url = sb.toString();
+
+ if (traceContext == null) {
+ traceContext = TraceContextProto.getDefaultInstance();
+ }
+
+ this.httpServletRequest =
+ new HttpServletRequestWrapper(httpServletRequest) {
+
+ @Override
+ public long getDateHeader(String name) {
+ return fields.getDateField(name);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return fields.get(name);
+ }
+
+ @Override
+ public Enumeration getHeaders(String name) {
+ return fields.getValues(name);
+ }
+
+ @Override
+ public Enumeration getHeaderNames() {
+ return fields.getFieldNames();
+ }
+
+ @Override
+ public int getIntHeader(String name) {
+ return Math.toIntExact(fields.getLongField(name));
+ }
+
+ @Override
+ public String getRequestURI() {
+ return httpUri.getPath();
+ }
+
+ @Override
+ public String getScheme() {
+ return httpUri.getScheme();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return isSecure;
+ }
+ };
+
+ this.baseRequest = request;
+ }
+
+ public Request getBaseRequest() {
+ return baseRequest;
+ }
+
+ public HttpServletRequest getHttpServletRequest() {
+ return httpServletRequest;
+ }
+
+ @Override
+ public Stream getHeadersList() {
+ return baseRequest.getHttpFields().stream()
+ .map(
+ f ->
+ HttpPb.ParsedHttpHeader.newBuilder()
+ .setKey(f.getName())
+ .setValue(f.getValue())
+ .build());
+ }
+
+ @Override
+ public String getUrl() {
+ return url;
+ }
+
+ @Override
+ public RuntimePb.UPRequest.RequestType getRequestType() {
+ return requestType;
+ }
+
+ @Override
+ public String getBackgroundRequestId() {
+ return backgroundRequestId;
+ }
+
+ @Override
+ public boolean hasTraceContext() {
+ return traceContext != null;
+ }
+
+ @Override
+ public TracePb.TraceContextProto getTraceContext() {
+ return traceContext;
+ }
+
+ @Override
+ public String getSecurityLevel() {
+ // TODO(b/78515194) Need to find a mapping for this field.
+ return null;
+ }
+
+ @Override
+ public boolean getIsOffline() {
+ return isOffline;
+ }
+
+ @Override
+ public String getAppId() {
+ return appInfoFactory.getGaeApplication();
+ }
+
+ @Override
+ public String getModuleId() {
+ return appInfoFactory.getGaeService();
+ }
+
+ @Override
+ public String getModuleVersionId() {
+ return appInfoFactory.getGaeServiceVersion();
+ }
+
+ @Override
+ public String getObfuscatedGaiaId() {
+ return obfuscatedGaiaId;
+ }
+
+ @Override
+ public String getUserOrganization() {
+ return userOrganization;
+ }
+
+ @Override
+ public boolean getIsTrustedApp() {
+ return isTrustedApp;
+ }
+
+ @Override
+ public boolean getTrusted() {
+ return isTrusted;
+ }
+
+ @Override
+ public String getPeerUsername() {
+ return peerUsername;
+ }
+
+ @Override
+ public long getGaiaId() {
+ return gaiaId;
+ }
+
+ @Override
+ public String getAuthuser() {
+ return authUser;
+ }
+
+ @Override
+ public String getGaiaSession() {
+ return gaiaSession;
+ }
+
+ @Override
+ public String getAppserverDatacenter() {
+ return appserverDataCenter;
+ }
+
+ @Override
+ public String getAppserverTaskBns() {
+ return appserverTaskBns;
+ }
+
+ @Override
+ public boolean hasEventIdHash() {
+ return eventIdHash != null;
+ }
+
+ @Override
+ public String getEventIdHash() {
+ return eventIdHash;
+ }
+
+ @Override
+ public boolean hasRequestLogId() {
+ return requestLogId != null;
+ }
+
+ @Override
+ public String getRequestLogId() {
+ return requestLogId;
+ }
+
+ @Override
+ public boolean hasDefaultVersionHostname() {
+ return defaultVersionHostname != null;
+ }
+
+ @Override
+ public String getDefaultVersionHostname() {
+ return defaultVersionHostname;
+ }
+
+ @Override
+ public boolean getIsAdmin() {
+ return isAdmin;
+ }
+
+ @Override
+ public String getEmail() {
+ return email;
+ }
+
+ @Override
+ public String getAuthDomain() {
+ return authDomain;
+ }
+
+ @Override
+ public String getSecurityTicket() {
+ return securityTicket;
+ }
+
+ public Duration getTimeRemaining() {
+ return duration;
+ }
+}
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyResponseAPIData.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyResponseAPIData.java
new file mode 100644
index 000000000..7a1ed5d53
--- /dev/null
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyResponseAPIData.java
@@ -0,0 +1,89 @@
+/*
+ * 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.apphosting.runtime.jetty9;
+
+import com.google.apphosting.base.protos.AppLogsPb;
+import com.google.apphosting.base.protos.RuntimePb;
+import com.google.apphosting.runtime.ResponseAPIData;
+import com.google.apphosting.runtime.anyrpc.AnyRpcServerContext;
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ByteString;
+import java.util.Collection;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.server.Response;
+
+public class JettyResponseAPIData implements ResponseAPIData {
+
+ private final Response response;
+ private final HttpServletResponse httpServletResponse;
+
+ public JettyResponseAPIData(Response response, HttpServletResponse httpServletResponse) {
+ this.response = response;
+ this.httpServletResponse = httpServletResponse;
+ }
+
+ public Response getResponse() {
+ return response;
+ }
+
+ public HttpServletResponse getHttpServletResponse() {
+ return httpServletResponse;
+ }
+
+ @Override
+ public void addAppLog(AppLogsPb.AppLogLine logLine) {}
+
+ @Override
+ public int getAppLogCount() {
+ return 0;
+ }
+
+ @Override
+ public List getAndClearAppLogList() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public void setSerializedTrace(ByteString byteString) {}
+
+ @Override
+ public void setTerminateClone(boolean terminateClone) {}
+
+ @Override
+ public void setCloneIsInUncleanState(boolean b) {}
+
+ @Override
+ public void setUserMcycles(long l) {}
+
+ @Override
+ public void addAllRuntimeLogLine(Collection logLines) {}
+
+ @Override
+ public void error(int error, String errorMessage) {}
+
+ @Override
+ public void finishWithResponse(AnyRpcServerContext rpc) {}
+
+ @Override
+ public void complete() {}
+
+ @Override
+ public int getError() {
+ return 0;
+ }
+}
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyServletEngineAdapter.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyServletEngineAdapter.java
index b1b205310..718e7b7ac 100644
--- a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyServletEngineAdapter.java
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/JettyServletEngineAdapter.java
@@ -16,15 +16,21 @@
package com.google.apphosting.runtime.jetty9;
+import static com.google.apphosting.runtime.AppEngineConstants.HTTP_CONNECTOR_MODE;
+import static com.google.apphosting.runtime.AppEngineConstants.IGNORE_RESPONSE_SIZE_LIMIT;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.apphosting.base.AppVersionKey;
import com.google.apphosting.base.protos.AppinfoPb;
+import com.google.apphosting.base.protos.EmptyMessage;
import com.google.apphosting.base.protos.RuntimePb.UPRequest;
import com.google.apphosting.base.protos.RuntimePb.UPResponse;
import com.google.apphosting.runtime.AppVersion;
+import com.google.apphosting.runtime.LocalRpcContext;
import com.google.apphosting.runtime.MutableUpResponse;
import com.google.apphosting.runtime.ServletEngineAdapter;
+import com.google.apphosting.runtime.SessionStoreFactory;
+import com.google.apphosting.runtime.anyrpc.EvaluationRuntimeServerInterface;
import com.google.apphosting.utils.config.AppEngineConfigException;
import com.google.apphosting.utils.config.AppYaml;
import com.google.common.flogger.GoogleLogger;
@@ -37,13 +43,14 @@
import java.util.Optional;
import javax.servlet.ServletException;
import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.SizeLimitHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
/**
* This is an implementation of ServletEngineAdapter that uses the third-party Jetty servlet engine.
- *
*/
public class JettyServletEngineAdapter implements ServletEngineAdapter {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
@@ -89,7 +96,8 @@ private static AppYaml getAppYaml(ServletEngineAdapter.Config runtimeOptions) {
try {
appYaml = AppYaml.parse(new InputStreamReader(new FileInputStream(appYamlFile), UTF_8));
} catch (FileNotFoundException | AppEngineConfigException e) {
- logger.atWarning().log("Failed to load app.yaml file at location %s - %s",
+ logger.atWarning().log(
+ "Failed to load app.yaml file at location %s - %s",
appYamlFile.getPath(), e.getMessage());
}
return appYaml;
@@ -102,10 +110,11 @@ public void start(String serverInfo, ServletEngineAdapter.Config runtimeOptions)
server.setConnectors(new Connector[] {rpcConnector});
AppVersionHandlerFactory appVersionHandlerFactory =
new AppVersionHandlerFactory(
- server, serverInfo, contextFactory, /*useJettyErrorPageHandler=*/ false);
+ server, serverInfo, contextFactory, /* useJettyErrorPageHandler= */ false);
appVersionHandlerMap = new AppVersionHandlerMap(appVersionHandlerFactory);
- if (!"java8".equals(System.getenv("GAE_RUNTIME"))) {
+ if (!Objects.equals(System.getenv("GAE_RUNTIME"), "java8")
+ && !Boolean.getBoolean(IGNORE_RESPONSE_SIZE_LIMIT)) {
SizeLimitHandler sizeLimitHandler = new SizeLimitHandler(-1, MAX_RESPONSE_SIZE);
sizeLimitHandler.setHandler(appVersionHandlerMap);
server.setHandler(sizeLimitHandler);
@@ -113,19 +122,57 @@ public void start(String serverInfo, ServletEngineAdapter.Config runtimeOptions)
server.setHandler(appVersionHandlerMap);
}
- if (runtimeOptions.useJettyHttpProxy()) {
- server.setAttribute(
- "com.google.apphosting.runtime.jetty9.appYaml",
- appYaml.orElseGet(() -> JettyServletEngineAdapter.getAppYaml(runtimeOptions)));
- JettyHttpProxy.startServer(runtimeOptions);
- }
-
try {
- server.start();
- } catch (Exception ex) {
- // TODO: Should we have a wrapper exception for this
- // type of thing in ServletEngineAdapter?
- throw new RuntimeException(ex);
+ boolean startJettyHttpProxy = false;
+ if (runtimeOptions.useJettyHttpProxy()) {
+ AppVersionKey appVersionKey;
+ /* The init actions are not done in the constructor as they are not used when testing */
+ String appRoot = runtimeOptions.applicationRoot();
+ String appPath = runtimeOptions.fixedApplicationPath();
+ AppInfoFactory appInfoFactory = new AppInfoFactory(System.getenv());
+ AppinfoPb.AppInfo appinfo = appInfoFactory.getAppInfoFromFile(appRoot, appPath);
+ // TODO Should we also call ApplyCloneSettings()?
+ LocalRpcContext context = new LocalRpcContext<>(EmptyMessage.class);
+ EvaluationRuntimeServerInterface evaluationRuntimeServerInterface =
+ Objects.requireNonNull(runtimeOptions.evaluationRuntimeServerInterface());
+ evaluationRuntimeServerInterface.addAppVersion(context, appinfo);
+ EmptyMessage unused = context.getResponse();
+
+ if (Boolean.getBoolean(HTTP_CONNECTOR_MODE)) {
+ logger.atInfo().log("Using HTTP_CONNECTOR_MODE to bypass RPC");
+ appVersionKey = AppVersionKey.fromAppInfo(appinfo);
+ Handler unused2 = appVersionHandlerMap.getHandler(appVersionKey);
+ JettyHttpProxy.insertHandlers(server);
+ AppVersion appVersion = appVersionHandlerMap.getAppVersion(appVersionKey);
+ server.insertHandler(
+ new JettyHttpHandler(runtimeOptions, appVersion, appVersionKey, appInfoFactory));
+ ServerConnector connector = JettyHttpProxy.newConnector(server, runtimeOptions);
+ server.addConnector(connector);
+ } else {
+ server.setAttribute(
+ "com.google.apphosting.runtime.jetty9.appYaml",
+ appYaml.orElseGet(() -> JettyServletEngineAdapter.getAppYaml(runtimeOptions)));
+ // Delay start of JettyHttpProxy until after the main server and application is started.
+ startJettyHttpProxy = true;
+ }
+ }
+
+ ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread()
+ .setContextClassLoader(JettyServletEngineAdapter.class.getClassLoader());
+ try {
+ server.start();
+ if (startJettyHttpProxy) {
+ JettyHttpProxy.startServer(runtimeOptions);
+ }
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+ }
+ } catch (Exception e) {
+ if (e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ throw new IllegalStateException(e);
}
}
@@ -152,10 +199,10 @@ public void deleteAppVersion(AppVersion appVersion) {
* Sets the {@link com.google.apphosting.runtime.SessionStoreFactory} that will be used to create
* the list of {@link com.google.apphosting.runtime.SessionStore SessionStores} to which the HTTP
* Session will be stored, if sessions are enabled. This method must be invoked after {@link
- * #start(String)}.
+ * #start(String, Config)}.
*/
@Override
- public void setSessionStoreFactory(com.google.apphosting.runtime.SessionStoreFactory factory) {
+ public void setSessionStoreFactory(SessionStoreFactory factory) {
appVersionHandlerMap.setSessionStoreFactory(factory);
}
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/ResourceFileServlet.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/ResourceFileServlet.java
index 68bddac18..6cea7c23e 100644
--- a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/ResourceFileServlet.java
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/ResourceFileServlet.java
@@ -17,7 +17,7 @@
package com.google.apphosting.runtime.jetty9;
import com.google.apphosting.runtime.AppVersion;
-import com.google.apphosting.runtime.JettyConstants;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.utils.config.AppYaml;
import com.google.common.base.Ascii;
import com.google.common.flogger.GoogleLogger;
@@ -68,11 +68,11 @@ public class ResourceFileServlet extends HttpServlet {
public void init() throws ServletException {
context = getServletContext();
AppVersion appVersion =
- (AppVersion) context.getAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR);
+ (AppVersion) context.getAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR);
chandler = ((ContextHandler.Context) context).getContextHandler();
AppYaml appYaml =
- (AppYaml) chandler.getServer().getAttribute(JettyConstants.APP_YAML_ATTRIBUTE_TARGET);
+ (AppYaml) chandler.getServer().getAttribute(AppEngineConstants.APP_YAML_ATTRIBUTE_TARGET);
fSender = new FileSender(appYaml);
// AFAICT, there is no real API to retrieve this information, so
// we access Jetty's internal state.
@@ -250,7 +250,7 @@ private boolean maybeServeWelcomeFile(
}
AppVersion appVersion =
- (AppVersion) getServletContext().getAttribute(JettyConstants.APP_VERSION_CONTEXT_ATTR);
+ (AppVersion) getServletContext().getAttribute(AppEngineConstants.APP_VERSION_CONTEXT_ATTR);
ServletHandler handler = chandler.getChildHandlerByClass(ServletHandler.class);
MappedResource defaultEntry = handler.getHolderEntry("/");
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/RpcConnection.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/RpcConnection.java
index ad5c18ac6..e69f7797d 100644
--- a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/RpcConnection.java
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/RpcConnection.java
@@ -21,7 +21,7 @@
import com.google.apphosting.base.protos.HttpPb.ParsedHttpHeader;
import com.google.apphosting.base.protos.RuntimePb.UPRequest;
import com.google.apphosting.base.protos.RuntimePb.UPResponse;
-import com.google.apphosting.runtime.JettyConstants;
+import com.google.apphosting.runtime.AppEngineConstants;
import com.google.apphosting.runtime.MutableUpResponse;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
@@ -245,7 +245,7 @@ public void onCompleted() {
// Tell AppVersionHandlerMap which app version should handle this
// request.
- request.setAttribute(JettyConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);
+ request.setAttribute(AppEngineConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);
final boolean skipAdmin = hasSkipAdminCheck(endPoint.getUpRequest());
// Translate the X-Google-Internal-SkipAdminCheck to a servlet attribute.
diff --git a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/UPRequestTranslator.java b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/UPRequestTranslator.java
index 4cdef9797..4673926df 100644
--- a/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/UPRequestTranslator.java
+++ b/runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/UPRequestTranslator.java
@@ -16,6 +16,38 @@
package com.google.apphosting.runtime.jetty9;
+import static com.google.apphosting.runtime.AppEngineConstants.BACKGROUND_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_ADMIN_HEADER_VALUE;
+import static com.google.apphosting.runtime.AppEngineConstants.IS_TRUSTED;
+import static com.google.apphosting.runtime.AppEngineConstants.PRIVATE_APPENGINE_HEADERS;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.WARMUP_REQUEST_URL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_API_TICKET;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_DATACENTER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_APPSERVER_TASK_BNS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_AUTH_DOMAIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_DEFAULT_VERSION_HOSTNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_AUTHUSER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_GAIA_SESSION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_HTTPS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_LOAS_PEER_USERNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_QUEUENAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_REQUEST_LOG_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TIMEOUT_MS;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_TRUSTED_IP_REQUEST;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_EMAIL;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ID;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IP;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_IS_ADMIN;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_NICKNAME;
+import static com.google.apphosting.runtime.AppEngineConstants.X_APPENGINE_USER_ORGANIZATION;
+import static com.google.apphosting.runtime.AppEngineConstants.X_CLOUD_TRACE_CONTEXT;
+import static com.google.apphosting.runtime.AppEngineConstants.X_FORWARDED_PROTO;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_PROFILER;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK;
+import static com.google.apphosting.runtime.AppEngineConstants.X_GOOGLE_INTERNAL_SKIPADMINCHECK_UC;
+
import com.google.apphosting.base.protos.AppinfoPb;
import com.google.apphosting.base.protos.HttpPb;
import com.google.apphosting.base.protos.HttpPb.HttpRequest;
@@ -26,13 +58,13 @@
import com.google.apphosting.runtime.TraceContextHelper;
import com.google.common.base.Ascii;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.GoogleLogger;
import com.google.common.html.HtmlEscapers;
import com.google.protobuf.ByteString;
import com.google.protobuf.TextFormat;
import java.io.IOException;
import java.util.Collections;
+import java.util.Objects;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -46,70 +78,6 @@ public class UPRequestTranslator {
private static final String DEFAULT_SECRET_KEY = "secretkey";
- /**
- * The HTTP headers that are handled specially by this proxy are defined in lowercae because HTTP
- * headers are case insensitive and we look then up in a set or switch after converting to
- * lower-case.
- */
- private static final String X_FORWARDED_PROTO = "x-forwarded-proto";
-
- private static final String X_APPENGINE_API_TICKET = "x-appengine-api-ticket";
- private static final String X_APPENGINE_HTTPS = "x-appengine-https";
- private static final String X_APPENGINE_USER_IP = "x-appengine-user-ip";
- private static final String X_APPENGINE_USER_EMAIL = "x-appengine-user-email";
- private static final String X_APPENGINE_AUTH_DOMAIN = "x-appengine-auth-domain";
- private static final String X_APPENGINE_USER_ID = "x-appengine-user-id";
- private static final String X_APPENGINE_USER_NICKNAME = "x-appengine-user-nickname";
- private static final String X_APPENGINE_USER_ORGANIZATION = "x-appengine-user-organization";
- private static final String X_APPENGINE_USER_IS_ADMIN = "x-appengine-user-is-admin";
- private static final String X_APPENGINE_TRUSTED_IP_REQUEST = "x-appengine-trusted-ip-request";
- private static final String X_APPENGINE_LOAS_PEER_USERNAME = "x-appengine-loas-peer-username";
- private static final String X_APPENGINE_GAIA_ID = "x-appengine-gaia-id";
- private static final String X_APPENGINE_GAIA_AUTHUSER = "x-appengine-gaia-authuser";
- private static final String X_APPENGINE_GAIA_SESSION = "x-appengine-gaia-session";
- private static final String X_APPENGINE_APPSERVER_DATACENTER = "x-appengine-appserver-datacenter";
- private static final String X_APPENGINE_APPSERVER_TASK_BNS = "x-appengine-appserver-task-bns";
- private static final String X_APPENGINE_DEFAULT_VERSION_HOSTNAME =
- "x-appengine-default-version-hostname";
- private static final String X_APPENGINE_REQUEST_LOG_ID = "x-appengine-request-log-id";
- private static final String X_APPENGINE_QUEUENAME = "x-appengine-queuename";
- private static final String X_APPENGINE_TIMEOUT_MS = "x-appengine-timeout-ms";
- private static final String X_GOOGLE_INTERNAL_SKIPADMINCHECK = "x-google-internal-skipadmincheck";
- private static final String X_GOOGLE_INTERNAL_SKIPADMINCHECK_UC =
- "X-Google-Internal-SkipAdminCheck";
- private static final String X_GOOGLE_INTERNAL_PROFILER = "x-google-internal-profiler";
- private static final String X_CLOUD_TRACE_CONTEXT = "x-cloud-trace-context";
-
- private static final String IS_ADMIN_HEADER_VALUE = "1";
- private static final String IS_TRUSTED = "1";
-
- // The impersonated IP address of warmup requests (and also background)
- // ()
- private static final String WARMUP_IP = "0.1.0.3";
-
- private static final ImmutableSet PRIVATE_APPENGINE_HEADERS =
- ImmutableSet.of(
- X_APPENGINE_API_TICKET,
- X_APPENGINE_HTTPS,
- X_APPENGINE_USER_IP,
- X_APPENGINE_USER_EMAIL,
- X_APPENGINE_AUTH_DOMAIN,
- X_APPENGINE_USER_ID,
- X_APPENGINE_USER_NICKNAME,
- X_APPENGINE_USER_ORGANIZATION,
- X_APPENGINE_USER_IS_ADMIN,
- X_APPENGINE_TRUSTED_IP_REQUEST,
- X_APPENGINE_LOAS_PEER_USERNAME,
- X_APPENGINE_GAIA_ID,
- X_APPENGINE_GAIA_AUTHUSER,
- X_APPENGINE_GAIA_SESSION,
- X_APPENGINE_APPSERVER_DATACENTER,
- X_APPENGINE_APPSERVER_TASK_BNS,
- X_APPENGINE_DEFAULT_VERSION_HOSTNAME,
- X_APPENGINE_REQUEST_LOG_ID,
- X_APPENGINE_TIMEOUT_MS,
- X_GOOGLE_INTERNAL_PROFILER);
-
private final AppInfoFactory appInfoFactory;
private final boolean passThroughPrivateHeaders;
private final boolean skipPostData;
@@ -230,12 +198,12 @@ public final RuntimePb.UPRequest translateRequest(HttpServletRequest realRequest
}
}
- if ("/_ah/background".equals(realRequest.getRequestURI())) {
- if (WARMUP_IP.equals(httpRequest.getUserIp())) {
+ if (Objects.equals(realRequest.getRequestURI(), BACKGROUND_REQUEST_URL)) {
+ if (Objects.equals(httpRequest.getUserIp(), WARMUP_IP)) {
upReqBuilder.setRequestType(UPRequest.RequestType.BACKGROUND);
}
- } else if ("/_ah/start".equals(realRequest.getRequestURI())) {
- if (WARMUP_IP.equals(httpRequest.getUserIp())) {
+ } else if (Objects.equals(realRequest.getRequestURI(), WARMUP_REQUEST_URL)) {
+ if (Objects.equals(httpRequest.getUserIp(), WARMUP_IP)) {
// This request came from within App Engine via secure internal channels; tell Jetty
// it's HTTPS to avoid 403 because of web.xml security-constraint checks.
httpRequest.setIsHttps(true);
diff --git a/runtime/test/pom.xml b/runtime/test/pom.xml
index eda026aea..e4c411510 100644
--- a/runtime/test/pom.xml
+++ b/runtime/test/pom.xml
@@ -22,7 +22,7 @@
com.google.appengineruntime-parent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/runtime/test/src/test/java/com/google/apphosting/runtime/jetty9/JavaRuntimeViaHttpBase.java b/runtime/test/src/test/java/com/google/apphosting/runtime/jetty9/JavaRuntimeViaHttpBase.java
index 03903e6ea..979a9c213 100644
--- a/runtime/test/src/test/java/com/google/apphosting/runtime/jetty9/JavaRuntimeViaHttpBase.java
+++ b/runtime/test/src/test/java/com/google/apphosting/runtime/jetty9/JavaRuntimeViaHttpBase.java
@@ -42,6 +42,7 @@
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ResourceInfo;
import com.google.common.reflect.Reflection;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.ForOverride;
import com.google.appengine.repackaged.com.google.protobuf.ByteString;
import com.google.appengine.repackaged.com.google.protobuf.ExtensionRegistry;
@@ -84,10 +85,12 @@ public abstract class JavaRuntimeViaHttpBase {
@ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder();
private static final String RUNTIME_LOCATION_ROOT = "java/com/google/apphosting";
static final int RESPONSE_200 = 200;
+
@FunctionalInterface
interface ApiServerFactory {
ApiServerT newApiServer(int apiPort, int runtimePort) throws IOException;
}
+
static class RuntimeContext implements AutoCloseable {
private final Process runtimeProcess;
private final ApiServerT httpApiServer;
@@ -95,6 +98,7 @@ static class RuntimeContext implements AutoCloseab
private final int jettyPort;
private final OutputPump outPump;
private final OutputPump errPump;
+
private RuntimeContext(
Process runtimeProcess,
ApiServerT httpApiServer,
@@ -109,14 +113,19 @@ private RuntimeContext(
this.outPump = outPump;
this.errPump = errPump;
}
+
public int getPort() {
return jettyPort;
}
+
@AutoValue
abstract static class Config {
abstract ImmutableMap environmentEntries();
+
abstract ImmutableList launcherFlags();
+
abstract ApiServerFactory apiServerFactory();
+
// The default configuration uses an API server that rejects all API calls as unknown.
// Individual tests can configure a different server, including the HttpApiServer from the SDK
// which provides APIs using their dev app server implementations.
@@ -125,35 +134,42 @@ static Builder builder() {
(apiPort, runtimePort) -> DummyApiServer.create(apiPort, ImmutableMap.of());
return builder(apiServerFactory);
}
+
static Builder builder(
ApiServerFactory apiServerFactory) {
return new AutoValue_JavaRuntimeViaHttpBase_RuntimeContext_Config.Builder()
.setEnvironmentEntries(ImmutableMap.of())
.setApiServerFactory(apiServerFactory);
}
+
@AutoValue.Builder
abstract static class Builder {
private boolean applicationPath;
private boolean applicationRoot;
+
/**
* Sets the application path. In this approach, applicationPath is the complete application
* location.
*/
+ @CanIgnoreReturnValue
Builder setApplicationPath(String path) {
applicationPath = true;
launcherFlagsBuilder().add("--fixed_application_path=" + path);
return this;
}
+
/** Sets Jetty's max request header size. */
Builder setJettyRequestHeaderSize(int size) {
launcherFlagsBuilder().add("--jetty_request_header_size=" + size);
return this;
}
+
/** Sets Jetty's max response header size. */
Builder setJettyResponseHeaderSize(int size) {
launcherFlagsBuilder().add("--jetty_response_header_size=" + size);
return this;
}
+
/**
* Sets the application root. In this legacy case, you need to set the correct set of env
* variables for "GAE_APPLICATION", "GAE_VERSION", "GAE_DEPLOYMENT_ID" with a correct
@@ -168,10 +184,15 @@ Builder setApplicationRoot(String root) {
launcherFlagsBuilder().add("--application_root=" + root);
return this;
}
+
abstract Builder setEnvironmentEntries(ImmutableMap entries);
+
abstract ImmutableList.Builder launcherFlagsBuilder();
+
abstract Builder setApiServerFactory(ApiServerFactory factory);
+
abstract Config autoBuild();
+
Config build() {
if (applicationPath == applicationRoot) {
throw new IllegalStateException(
@@ -181,6 +202,7 @@ Config build() {
}
}
}
+
/** JVM flags needed for JDK above JDK8 */
private static ImmutableList optionalFlags() {
if (!JAVA_VERSION.value().startsWith("1.8")) {
@@ -197,6 +219,7 @@ private static ImmutableList optionalFlags() {
}
return ImmutableList.of("-showversion"); // Just so that the list is not empty.
}
+
static RuntimeContext create(
Config config) throws IOException, InterruptedException {
PortPicker portPicker = PortPicker.create();
@@ -212,11 +235,11 @@ static RuntimeContext create(
.isTrue();
InetSocketAddress apiSocketAddress = new InetSocketAddress(apiPort);
ImmutableList.Builder builder = ImmutableList.builder();
- builder.add(JAVA_HOME.value() + "/bin/java");
- Integer debugPort = Integer.getInteger("appengine.debug.port");
- if (debugPort != null) {
+ builder.add(JAVA_HOME.value() + "/bin/java");
+ Integer debugPort = Integer.getInteger("appengine.debug.port");
+ if (debugPort != null) {
builder.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:" + debugPort);
- }
+ }
ImmutableList runtimeArgs =
builder
.add(
@@ -225,6 +248,8 @@ static RuntimeContext create(
"-Dappengine.use.EE10=" + Boolean.getBoolean("appengine.use.EE10"),
"-Dappengine.use.HttpConnector="
+ Boolean.getBoolean("appengine.use.HttpConnector"),
+ "-Dappengine.ignore.responseSizeLimit="
+ + Boolean.getBoolean("appengine.ignore.responseSizeLimit"),
"-Djetty.server.dumpAfterStart="
+ Boolean.getBoolean("jetty.server.dumpAfterStart"),
"-Duse.mavenjars=" + useMavenJars(),
@@ -262,35 +287,41 @@ static RuntimeContext create(
return new RuntimeContext<>(
runtimeProcess, httpApiServer, httpClient, jettyPort, outPump, errPump);
}
+
public static boolean isPortAvailable(String host, int port) {
- try {
- Socket socket = new Socket(host, port);
- socket.close();
- return true;
- } catch (Exception e) {
- return false;
- }
+ try {
+ try (Socket socket = new Socket(host, port)) {}
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
}
+
private static List jvmFlagsFromEnvironment(ImmutableMap env) {
return Splitter.on(' ').omitEmptyStrings().splitToList(env.getOrDefault("GAE_JAVA_OPTS", ""));
}
+
ApiServerT getApiServer() {
return httpApiServer;
}
+
HttpClient getHttpClient() {
return httpClient;
}
+
String jettyUrl(String urlPath) {
return String.format(
"http://%s%s",
HostAndPort.fromParts(new InetSocketAddress(jettyPort).getHostString(), jettyPort),
urlPath);
}
+
void executeHttpGet(String url, String expectedResponseBody, int expectedReturnCode)
throws Exception {
executeHttpGetWithRetries(
url, expectedResponseBody, expectedReturnCode, /* numberOfRetries= */ 1);
}
+
String executeHttpGet(String urlPath, int expectedReturnCode) throws Exception {
HttpGet get = new HttpGet(jettyUrl(urlPath));
HttpResponse response = httpClient.execute(get);
@@ -306,6 +337,7 @@ String executeHttpGet(String urlPath, int expectedReturnCode) throws Exception {
EntityUtils.consumeQuietly(entity);
}
}
+
void executeHttpGetWithRetries(
String urlPath, String expectedResponse, int expectedReturnCode, int numberOfRetries)
throws Exception {
@@ -324,12 +356,15 @@ void executeHttpGetWithRetries(
assertThat(content).isEqualTo(expectedResponse);
assertThat(retCode).isEqualTo(expectedReturnCode);
}
+
void awaitStdoutLineMatching(String pattern, long timeoutSeconds) throws InterruptedException {
outPump.awaitOutputLineMatching(pattern, timeoutSeconds);
}
+
void awaitStderrLineMatching(String pattern, long timeoutSeconds) throws InterruptedException {
errPump.awaitOutputLineMatching(pattern, timeoutSeconds);
}
+
private static Process launchRuntime(
ImmutableList args, ImmutableMap environmentEntries)
throws IOException {
@@ -337,29 +372,36 @@ private static Process launchRuntime(
pb.environment().putAll(environmentEntries);
return pb.start();
}
+
Process runtimeProcess() {
return runtimeProcess;
}
+
@Override
public void close() throws IOException {
runtimeProcess.destroy();
httpApiServer.close();
}
}
+
static boolean useMavenJars() {
return Boolean.getBoolean("use.mavenjars");
}
+
static boolean useJetty94LegacyMode() {
return Boolean.getBoolean("com.google.apphosting.runtime.jetty94.LEGACY_MODE");
}
+
static class OutputPump implements Runnable {
private final BufferedReader stream;
private final String echoPrefix;
private final BlockingQueue outputQueue = new LinkedBlockingQueue<>();
+
OutputPump(InputStream instream, String echoPrefix) {
this.stream = new BufferedReader(new InputStreamReader(instream, UTF_8));
this.echoPrefix = echoPrefix;
}
+
@Override
public void run() {
String line;
@@ -372,6 +414,7 @@ public void run() {
throw new RuntimeException(e);
}
}
+
void awaitOutputLineMatching(String pattern, long timeoutSeconds) throws InterruptedException {
long timeoutMillis = MILLISECONDS.convert(timeoutSeconds, SECONDS);
long deadline = System.currentTimeMillis() + timeoutMillis;
@@ -387,8 +430,10 @@ void awaitOutputLineMatching(String pattern, long timeoutSeconds) throws Interru
}
}
}
+
private static final Pattern JAR_URL_PATTERN = Pattern.compile("jar:file:(.*)!(.*)");
private static final Pattern JAR_FILE_URL_PATTERN = Pattern.compile("file:(.*\\.jar)");
+
/**
* Extract the test app with the given name into the given output directory. The situation is that
* we have a jar file in our classpath that contains this app, plus maybe a bunch of other stuff.
@@ -448,6 +493,7 @@ static void copyAppToDir(String appName, Path dir) throws IOException {
}
}
}
+
private static void copyJarContainingClass(String className, Path toPath) throws IOException {
try {
Class> threadManager = Class.forName(className);
@@ -469,6 +515,7 @@ private static void copyJarContainingClass(String className, Path toPath) throws
// OK: the app presumably doesn't need this.
}
}
+
/**
* An API server that handles API calls via the supplied handler map. Each incoming API call is
* looked up as {@code service.method}, for example {@code urlfetch.Fetch}, to discover a
@@ -480,11 +527,13 @@ private static void copyJarContainingClass(String className, Path toPath) throws
*/
static class DummyApiServer implements Closeable {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
+
static DummyApiServer create(
int apiPort, ImmutableMap> handlerMap)
throws IOException {
return create(apiPort, handlerMap, request -> {});
}
+
static DummyApiServer create(
int apiPort,
ImmutableMap> handlerMap,
@@ -498,13 +547,16 @@ static DummyApiServer create(
httpServer.start();
return apiServer;
}
+
private final HttpServer httpServer;
private final Function> handlerLookup;
private final Consumer requestObserver;
+
DummyApiServer(
HttpServer httpServer, Function> handlerLookup) {
this(httpServer, handlerLookup, request -> {});
}
+
private DummyApiServer(
HttpServer httpServer,
Function> handlerLookup,
@@ -513,14 +565,17 @@ private DummyApiServer(
this.handlerLookup = handlerLookup;
this.requestObserver = requestObserver;
}
+
@Override
public void close() {
httpServer.stop(0);
}
+
@ForOverride
RemoteApiPb.Response.Builder newResponseBuilder() {
return RemoteApiPb.Response.newBuilder();
}
+
void handle(HttpExchange exchange) throws IOException {
try (InputStream in = exchange.getRequestBody();
OutputStream out = exchange.getResponseBody()) {
diff --git a/runtime/test/src/test/java/com/google/apphosting/runtime/jetty9/SizeLimitIgnoreTest.java b/runtime/test/src/test/java/com/google/apphosting/runtime/jetty9/SizeLimitIgnoreTest.java
new file mode 100644
index 000000000..d02973cda
--- /dev/null
+++ b/runtime/test/src/test/java/com/google/apphosting/runtime/jetty9/SizeLimitIgnoreTest.java
@@ -0,0 +1,370 @@
+/*
+ * 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.apphosting.runtime.jetty9;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.lessThan;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.zip.GZIPOutputStream;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.ByteBufferContentProvider;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.InputStreamContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Utf8StringBuilder;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SizeLimitIgnoreTest extends JavaRuntimeViaHttpBase {
+ @Parameterized.Parameters
+ public static List
If authentication is required but the request comes from an untrusted ip, 307s the request
- * back to the trusted appserver. Otherwise it will auth the request and return a login url if
+ * back to the trusted appserver. Otherwise, it will auth the request and return a login url if
* needed.
*
*
From org.eclipse.jetty.server.Authentication:
@@ -165,18 +162,18 @@ public Constraint.Authorization getConstraintAuthentication(
* @param req The request
* @param res The response
* @param cb The callback
- * @throws ServerAuthException if an error occurred
*/
@Override
- public AuthenticationState validateRequest(Request req, Response res, Callback cb)
- throws ServerAuthException {
-
- ServletContextRequest contextRequest = Request.as(req, ServletContextRequest.class);
- HttpServletRequest request = contextRequest.getServletApiRequest();
- HttpServletResponse response = contextRequest.getHttpServletResponse();
-
- if (response == null) {
- throw new ServerAuthException("validateRequest called with null response!!!");
+ public AuthenticationState validateRequest(Request req, Response res, Callback cb) {
+ UserService userService = UserServiceFactory.getUserService();
+ // If the user is authenticated already, just create a
+ // AppEnginePrincipal or AppEngineFederatedPrincipal for them.
+ if (userService.isUserLoggedIn()) {
+ UserIdentity user = _loginService.login(null, null, null, null);
+ logger.atFine().log("authenticate() returning new principal for %s", user);
+ if (user != null) {
+ return new UserAuthenticationSucceeded(getAuthenticationType(), user);
+ }
}
if (AuthenticationState.Deferred.isDeferred(res)) {
@@ -184,46 +181,21 @@ public AuthenticationState validateRequest(Request req, Response res, Callback c
}
try {
- UserService userService = UserServiceFactory.getUserService();
- // If the user is authenticated already, just create a
- // AppEnginePrincipal or AppEngineFederatedPrincipal for them.
- if (userService.isUserLoggedIn()) {
- UserIdentity user = _loginService.login(null, null, null, null);
- logger.atFine().log("authenticate() returning new principal for %s", user);
- if (user != null) {
- return new UserAuthenticationSucceeded(getAuthenticationType(), user);
- }
- }
-
- try {
- logger.atFine().log(
- "Got %s but no one was logged in, redirecting.", request.getRequestURI());
- String url = userService.createLoginURL(getFullURL(request));
- response.sendRedirect(url);
- // Tell Jetty that we've already committed a response here.
- return AuthenticationState.CHALLENGE;
- } catch (ApiProxy.ApiProxyException ex) {
- // If we couldn't get a login URL for some reason, return a 403 instead.
- logger.atSevere().withCause(ex).log("Could not get login URL:");
- response.sendError(HttpServletResponse.SC_FORBIDDEN);
- return AuthenticationState.SEND_FAILURE;
- }
- } catch (IOException ex) {
- throw new ServerAuthException(ex);
+ logger.atFine().log(
+ "Got %s but no one was logged in, redirecting.", req.getHttpURI().getPath());
+ String url = userService.createLoginURL(HttpURI.build(req.getHttpURI()).asString());
+ Response.sendRedirect(req, res, cb, url);
+ // Tell Jetty that we've already committed a response here.
+ return AuthenticationState.CHALLENGE;
+ } catch (ApiProxy.ApiProxyException ex) {
+ // If we couldn't get a login URL for some reason, return a 403 instead.
+ logger.atSevere().withCause(ex).log("Could not get login URL:");
+ Response.writeError(req, res, cb, HttpServletResponse.SC_FORBIDDEN);
+ return AuthenticationState.SEND_FAILURE;
}
}
}
- /** Returns the full URL of the specified request, including any query string. */
- private static String getFullURL(HttpServletRequest request) {
- StringBuffer buffer = request.getRequestURL();
- if (request.getQueryString() != null) {
- buffer.append('?');
- buffer.append(request.getQueryString());
- }
- return buffer.toString();
- }
-
/**
* {@code AppEngineLoginService} is a custom Jetty {@link LoginService} that is aware of the two
* special role names implemented by Google App Engine. Any authenticated user is a member of the
@@ -279,13 +251,6 @@ public void setIdentityService(IdentityService identityService) {
this.identityService = identityService;
}
- /**
- * Validate a user identity. Validate that a UserIdentity previously created by a call to {@link
- * #login(String, Object, ServletRequest)} is still valid.
- *
- * @param user The user to validate
- * @return true if authentication has not been revoked for the user.
- */
@Override
public boolean validate(UserIdentity user) {
logger.atInfo().log("validate(%s) throwing UnsupportedOperationException.", user);
@@ -310,7 +275,7 @@ public User getUser() {
@Override
public String getName() {
- if ((user.getFederatedIdentity() != null) && (user.getFederatedIdentity().length() > 0)) {
+ if ((user.getFederatedIdentity() != null) && (!user.getFederatedIdentity().isEmpty())) {
return user.getFederatedIdentity();
}
return user.getEmail();
diff --git a/shared_sdk_jetty9/pom.xml b/shared_sdk_jetty9/pom.xml
index 4f57255f4..f6bad9cca 100644
--- a/shared_sdk_jetty9/pom.xml
+++ b/shared_sdk_jetty9/pom.xml
@@ -21,7 +21,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTjar
diff --git a/utils/pom.xml b/utils/pom.xml
index 80a8d53f1..354d12d4e 100644
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -21,7 +21,7 @@
com.google.appengineparent
- 2.0.28-SNAPSHOT
+ 2.0.29-SNAPSHOTtrue