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.appengine appengine-api-1.0-sdk - 2.0.27 + 2.0.28 javax.servlet @@ -89,7 +89,7 @@ Source code for all public APIs for com.google.appengine.api.* packages. com.google.appengine appengine-api-1.0-sdk - 2.0.27 + 2.0.28 javax.servlet @@ -188,7 +188,7 @@ Source code for remote APIs for App Engine. com.google.appengine appengine-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.appengine appengine-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.appengine appengine-testing - 2.0.27 + 2.0.28 test com.google.appengine appengine-api-stubs - 2.0.27 + 2.0.28 test com.google.appengine appengine-tools-sdk - 2.0.27 + 2.0.28 test ``` 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-SNAPSHOT target/${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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT true @@ -239,7 +239,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.7.0 com.microsoft.doclet.DocFxDoclet false 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar AppEngine :: 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar AppEngine :: 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.appengine appengine_setup - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT 4.0.0 @@ -36,7 +36,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.3.0 org.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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT apiserver_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.appengine appengine_setup - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT 4.0.0 com.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 @@ testapps com.google.appengine - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT 4.0.0 com.google.appengine.setup.testapps jetty12_testapp - 12.0.10 + 12.0.11 1.9.22.1 com.google.appengine.setup.testapps testapps_common - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT org.eclipse.jetty @@ -106,7 +106,7 @@ srirammahavadi-dev GCLOUD_CONFIG true - ${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.appengine appengine_setup - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT 4.0.0 com.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.testapps springboot_testapp - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT springboot_testapp Demo project for Spring Boot @@ -44,12 +44,12 @@ com.google.appengine appengine-api-1.0-sdk - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT com.google.appengine.setup.testapps testapps_common - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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 @@ testapps com.google.appengine - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT 4.0.0 com.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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT pom 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.appengine applications - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT @@ -38,7 +38,7 @@ us-central1 prober-user prober_connectivity_test_database - + 2.50.0 ${project.version} UTF-8 target/${project.artifactId}-${project.version} @@ -58,22 +58,22 @@ com.google.cloud google-cloud-spanner - 6.68.1 + 6.71.0 com.google.api gax - 2.49.0 + ${gax.version} com.google.api gax-httpjson - 2.49.0 + ${gax.version} com.google.api gax-grpc - 2.49.0 + ${gax.version} com.google.api-client @@ -86,27 +86,27 @@ com.google.cloud google-cloud-bigquery - 2.40.2 + 2.41.0 com.google.cloud google-cloud-core - 2.39.0 + 2.40.0 com.google.cloud google-cloud-datastore - 2.20.1 + 2.20.2 com.google.cloud google-cloud-logging - 3.18.0 + 3.19.0 com.google.cloud google-cloud-storage - 2.39.0 + 2.40.1 com.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.appengine applications - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine e2etests - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT AppEngine :: e2e tests pom 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.appengine e2etests - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine e2etests - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT pom 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war AppEngine :: 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT AppEngine :: 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war 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.appengine testlocalapps - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT war AppEngine :: 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.appengine parent - 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT 4.0.0 jetty12-assembly @@ -36,7 +36,7 @@ maven-dependency-plugin - 3.6.1 + 3.7.1 unpack 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT pom 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.appengine lib-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine lib-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar AppEngine :: 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.appengine lib-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar AppEngine :: 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar AppEngine :: 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar AppEngine :: 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.0 com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT pom AppEngine :: Parent project @@ -62,8 +62,10 @@ 1.8 1.8 UTF-8 - 9.4.54.v20240208 - 12.0.10 + 9.4.55.v20240627 + 12.0.11 + 1.65.0 + 4.1.111.Final 2.0.13 https://oss.sonatype.org/content/repositories/google-snapshots/ sonatype-nexus-snapshots @@ -208,7 +210,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.3.0 ../deployment/target/runtime-deployment-${project.version} @@ -234,7 +236,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.3.0 ../deployment/target/runtime-deployment-${project.version} @@ -415,7 +417,7 @@ org.easymock easymock - 5.2.0 + 5.3.0 com.google.appengine @@ -565,7 +567,7 @@ org.apache.maven maven-core - 3.9.7 + 3.9.8 org.apache.ant @@ -581,12 +583,12 @@ org.apache.maven maven-plugin-api - 3.9.7 + 3.9.8 org.checkerframework checker-qual - 3.44.0 + 3.45.0 provided @@ -650,63 +652,63 @@ io.grpc grpc-api - 1.64.0 + ${io.grpc} io.grpc grpc-stub - 1.64.0 + ${io.grpc} io.grpc grpc-protobuf - 1.64.0 + ${io.grpc} io.grpc grpc-netty - 1.64.0 + ${io.grpc} io.netty netty-buffer - 4.1.110.Final + ${io.netty} io.netty netty-codec - 4.1.110.Final + ${io.netty} io.netty netty-codec-http - 4.1.110.Final + ${io.netty} io.netty netty-codec-http2 - 4.1.110.Final + ${io.netty} io.netty netty-common - 4.1.110.Final + ${io.netty} io.netty netty-handler - 4.1.110.Final + ${io.netty} io.netty netty-transport - 4.1.110.Final + ${io.netty} io.netty netty-transport-native-unix-common - 4.1.110.Final + ${io.netty} org.apache.tomcat @@ -716,7 +718,7 @@ com.fasterxml.jackson.core jackson-core - 2.17.1 + 2.17.2 joda-time @@ -743,13 +745,13 @@ com.google.truth truth - 1.4.2 + 1.4.3 test com.google.truth.extensions truth-java8-extension - 1.4.2 + 1.4.3 test @@ -775,7 +777,7 @@ com.google.cloud google-cloud-logging - 3.18.0 + 3.19.0 @@ -784,7 +786,7 @@ org.codehaus.mojo versions-maven-plugin - 2.16.2 + 2.17.0 file:///${session.executionRootDirectory}/maven-version-rules.xml false @@ -840,7 +842,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.1 + 3.1.0 false deploy @@ -939,7 +941,7 @@ org.codehaus.mojo versions-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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar AppEngine :: 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT com.google.appengine.demos annotationscanningwebapp 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT pom 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT com.google.appengine.demos failinitfilterwebapp 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar @@ -246,7 +246,7 @@ org.apache.maven.plugins maven-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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT com.google.appengine.demos nogaeapiswebapp 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT AppEngine :: runtime projects pom 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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar @@ -594,7 +594,7 @@ org.apache.maven.plugins maven-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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar @@ -483,7 +483,7 @@ org.apache.maven.plugins maven-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.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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 data() { + return Arrays.asList( + new Object[][] { + {"jetty94", false}, + {"ee8", false}, + {"ee10", false}, + {"ee8", true}, + {"ee10", true}, + }); + } + + private static final int MAX_SIZE = 32 * 1024 * 1024; + @Rule public TemporaryFolder temp = new TemporaryFolder(); + private final HttpClient httpClient = new HttpClient(); + private final boolean httpMode; + private final String environment; + private RuntimeContext runtime; + + public SizeLimitIgnoreTest(String environment, boolean httpMode) { + this.environment = environment; + this.httpMode = httpMode; + System.setProperty("appengine.use.HttpConnector", Boolean.toString(httpMode)); + System.setProperty("appengine.ignore.responseSizeLimit", "true"); + } + + @Before + public void before() throws Exception { + String app = "sizelimit" + environment; + copyAppToDir(app, temp.getRoot().toPath()); + httpClient.start(); + runtime = runtimeContext(); + assertEnvironment(); + System.err.println("==== Using Environment: " + environment + " " + httpMode + " ===="); + } + + @After + public void after() throws Exception { + httpClient.stop(); + runtime.close(); + } + + @Test + public void testResponseContentBelowMaxLength() throws Exception { + long contentLength = MAX_SIZE; + String url = runtime.jettyUrl("/?size=" + contentLength); + CompletableFuture completionListener = new CompletableFuture<>(); + AtomicLong contentReceived = new AtomicLong(); + httpClient + .newRequest(url) + .onResponseContentAsync( + (response, content, callback) -> { + contentReceived.addAndGet(content.remaining()); + callback.succeeded(); + }) + .header("setCustomHeader", "true") + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + assertThat(result.getResponse().getStatus(), equalTo(HttpStatus.OK_200)); + assertThat(contentReceived.get(), equalTo(contentLength)); + assertThat(result.getResponse().getHeaders().get("custom-header"), equalTo("true")); + } + + @Test + public void testResponseContentAboveMaxLength() throws Exception { + long contentLength = MAX_SIZE + 1; + String url = runtime.jettyUrl("/?size=" + contentLength); + CompletableFuture completionListener = new CompletableFuture<>(); + AtomicLong contentReceived = new AtomicLong(); + httpClient + .newRequest(url) + .onResponseContentAsync( + (response, content, callback) -> { + contentReceived.addAndGet(content.remaining()); + callback.succeeded(); + }) + .header("setCustomHeader", "true") + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + assertThat(result.getResponse().getStatus(), equalTo(HttpStatus.OK_200)); + assertThat(contentReceived.get(), equalTo(contentLength)); + assertThat(result.getResponse().getHeaders().get("custom-header"), equalTo("true")); + } + + @Test + public void testResponseContentBelowMaxLengthGzip() throws Exception { + long contentLength = MAX_SIZE; + String url = runtime.jettyUrl("/?size=" + contentLength); + CompletableFuture completionListener = new CompletableFuture<>(); + AtomicLong contentReceived = new AtomicLong(); + httpClient.getContentDecoderFactories().clear(); + httpClient + .newRequest(url) + .onResponseContentAsync( + (response, content, callback) -> { + contentReceived.addAndGet(content.remaining()); + callback.succeeded(); + }) + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + assertThat(result.getResponse().getHeaders().get(HttpHeader.CONTENT_ENCODING), equalTo("gzip")); + assertThat(result.getResponse().getStatus(), equalTo(HttpStatus.OK_200)); + assertThat(contentReceived.get(), lessThan(contentLength)); + } + + @Test + public void testResponseContentAboveMaxLengthGzip() throws Exception { + long contentLength = MAX_SIZE + 1; + String url = runtime.jettyUrl("/?size=" + contentLength); + CompletableFuture completionListener = new CompletableFuture<>(); + AtomicLong contentReceived = new AtomicLong(); + httpClient.getContentDecoderFactories().clear(); + httpClient + .newRequest(url) + .onResponseContentAsync( + (response, content, callback) -> { + contentReceived.addAndGet(content.remaining()); + callback.succeeded(); + }) + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + assertThat(result.getResponse().getHeaders().get(HttpHeader.CONTENT_ENCODING), equalTo("gzip")); + assertThat(result.getResponse().getStatus(), equalTo(HttpStatus.OK_200)); + assertThat(contentReceived.get(), lessThan(contentLength)); + } + + @Test + public void testRequestContentBelowMaxLength() throws Exception { + int contentLength = MAX_SIZE; + + byte[] data = new byte[contentLength]; + Arrays.fill(data, (byte) 'X'); + ContentProvider content = new ByteBufferContentProvider(BufferUtil.toBuffer(data)); + String url = runtime.jettyUrl("/"); + ContentResponse response = httpClient.newRequest(url).content(content).send(); + + assertThat(response.getStatus(), equalTo(HttpStatus.OK_200)); + assertThat( + response.getContentAsString(), containsString("RequestContentLength: " + contentLength)); + } + + @Test + public void testRequestContentAboveMaxLength() throws Exception { + int contentLength = MAX_SIZE + 1; + + CompletableFuture completionListener = new CompletableFuture<>(); + byte[] data = new byte[contentLength]; + Arrays.fill(data, (byte) 'X'); + Utf8StringBuilder received = new Utf8StringBuilder(); + ContentProvider content = new ByteBufferContentProvider(BufferUtil.toBuffer(data)); + String url = runtime.jettyUrl("/"); + httpClient + .newRequest(url) + .content(content) + .onResponseContentAsync( + (response, content1, callback) -> { + received.append(content1); + callback.succeeded(); + }) + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + assertThat(result.getResponse().getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413)); + + // If there is no Content-Length header the SizeLimitHandler fails the response as well. + if (result.getResponseFailure() == null) { + assertThat(received.toString(), containsString("Request body is too large")); + } + } + + @Test + public void testRequestContentBelowMaxLengthGzip() throws Exception { + int contentLength = MAX_SIZE; + + CompletableFuture completionListener = new CompletableFuture<>(); + byte[] data = new byte[contentLength]; + Arrays.fill(data, (byte) 'X'); + Utf8StringBuilder received = new Utf8StringBuilder(); + ContentProvider content = new InputStreamContentProvider(gzip(data)); + + String url = runtime.jettyUrl("/"); + httpClient + .newRequest(url) + .content(content) + .onResponseContentAsync( + (response, content1, callback) -> { + received.append(content1); + callback.succeeded(); + }) + .header(HttpHeader.CONTENT_ENCODING, "gzip") + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + assertThat(result.getResponse().getStatus(), equalTo(HttpStatus.OK_200)); + assertThat(received.toString(), containsString("RequestContentLength: " + contentLength)); + } + + @Test + public void testRequestContentAboveMaxLengthGzip() throws Exception { + int contentLength = MAX_SIZE + 1; + + CompletableFuture completionListener = new CompletableFuture<>(); + byte[] data = new byte[contentLength]; + Arrays.fill(data, (byte) 'X'); + Utf8StringBuilder received = new Utf8StringBuilder(); + ContentProvider content = new InputStreamContentProvider(gzip(data)); + + String url = runtime.jettyUrl("/"); + httpClient + .newRequest(url) + .content(content) + .onResponseContentAsync( + (response, content1, callback) -> { + received.append(content1); + callback.succeeded(); + }) + .header(HttpHeader.CONTENT_ENCODING, "gzip") + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + assertThat(result.getResponse().getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413)); + + // If there is no Content-Length header the SizeLimitHandler fails the response as well. + if (result.getResponseFailure() == null) { + assertThat(received.toString(), containsString("Request body is too large")); + } + } + + @Test + public void testResponseContentLengthHeader() throws Exception { + long contentLength = MAX_SIZE + 1; + String url = runtime.jettyUrl("/?setContentLength=" + contentLength); + httpClient.getContentDecoderFactories().clear(); + ContentResponse response = httpClient.newRequest(url).send(); + + assertThat(response.getStatus(), equalTo(HttpStatus.INTERNAL_SERVER_ERROR_500)); + + // No content is sent on the Jetty 9.4 runtime. + if (!Objects.equals(environment, "jetty94")) { + assertThat(response.getContentAsString(), containsString("IllegalStateException")); + } + } + + @Test + public void testRequestContentLengthHeader() throws Exception { + CompletableFuture completionListener = new CompletableFuture<>(); + DeferredContentProvider provider = new DeferredContentProvider(ByteBuffer.allocate(1)); + int contentLength = MAX_SIZE + 1; + String url = runtime.jettyUrl("/"); + Utf8StringBuilder received = new Utf8StringBuilder(); + httpClient + .newRequest(url) + .header(HttpHeader.CONTENT_LENGTH, Long.toString(contentLength)) + .header("foo", "bar") + .content(provider) + .onResponseContentAsync( + (response, content, callback) -> { + received.append(content); + callback.succeeded(); + provider.close(); + }) + .send(completionListener::complete); + + Result result = completionListener.get(5, TimeUnit.SECONDS); + Response response = result.getResponse(); + assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413)); + + // If there is no Content-Length header the SizeLimitHandler fails the response as well. + if (result.getResponseFailure() == null) { + assertThat(received.toString(), containsString("Request body is too large")); + } + } + + private RuntimeContext runtimeContext() throws Exception { + RuntimeContext.Config config = + RuntimeContext.Config.builder().setApplicationPath(temp.getRoot().toString()).build(); + return RuntimeContext.create(config); + } + + private void assertEnvironment() throws Exception { + String match; + switch (environment) { + case "jetty94": + match = "org.eclipse.jetty.server.Request"; + break; + case "ee8": + match = "org.eclipse.jetty.ee8"; + break; + case "ee10": + match = "org.eclipse.jetty.ee10"; + break; + default: + throw new IllegalArgumentException(environment); + } + + String runtimeUrl = runtime.jettyUrl("/?getRequestClass=true"); + ContentResponse response = httpClient.GET(runtimeUrl); + assertThat(response.getContentAsString(), containsString(match)); + } + + private static InputStream gzip(byte[] data) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) { + gzipOutputStream.write(data); + } + return new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); + } +} diff --git a/runtime/testapps/pom.xml b/runtime/testapps/pom.xml index bf52e7696..834a2846b 100644 --- a/runtime/testapps/pom.xml +++ b/runtime/testapps/pom.xml @@ -22,7 +22,7 @@ com.google.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/runtime/util/pom.xml b/runtime/util/pom.xml index c4ddcac05..d867bc722 100644 --- a/runtime/util/pom.xml +++ b/runtime/util/pom.xml @@ -22,7 +22,7 @@ com.google.appengine runtime-parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/runtime_shared/pom.xml b/runtime_shared/pom.xml index 815ec9b3c..6c488a1a2 100644 --- a/runtime_shared/pom.xml +++ b/runtime_shared/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/runtime_shared_jetty12/pom.xml b/runtime_shared_jetty12/pom.xml index 6ad5ed832..fec0b0152 100644 --- a/runtime_shared_jetty12/pom.xml +++ b/runtime_shared_jetty12/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/runtime_shared_jetty12_ee10/pom.xml b/runtime_shared_jetty12_ee10/pom.xml index da43d0e33..123cd5b43 100644 --- a/runtime_shared_jetty12_ee10/pom.xml +++ b/runtime_shared_jetty12_ee10/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/runtime_shared_jetty9/pom.xml b/runtime_shared_jetty9/pom.xml index d74fd5693..e0ebea442 100644 --- a/runtime_shared_jetty9/pom.xml +++ b/runtime_shared_jetty9/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/sdk_assembly/pom.xml b/sdk_assembly/pom.xml index 4dc83b389..78c501f8d 100644 --- a/sdk_assembly/pom.xml +++ b/sdk_assembly/pom.xml @@ -20,7 +20,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT 4.0.0 appengine-java-sdk @@ -81,7 +81,7 @@ maven-dependency-plugin - 3.6.1 + 3.7.1 unpack diff --git a/sessiondata/pom.xml b/sessiondata/pom.xml index d08c9b45f..7ba9d6c3e 100644 --- a/sessiondata/pom.xml +++ b/sessiondata/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/shared_sdk/pom.xml b/shared_sdk/pom.xml index e2ab698d4..f89e82601 100644 --- a/shared_sdk/pom.xml +++ b/shared_sdk/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/shared_sdk_jetty12/pom.xml b/shared_sdk_jetty12/pom.xml index a8401ff94..5ad37beee 100644 --- a/shared_sdk_jetty12/pom.xml +++ b/shared_sdk_jetty12/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar diff --git a/shared_sdk_jetty12/src/main/java/com/google/apphosting/runtime/jetty/EE10AppEngineAuthentication.java b/shared_sdk_jetty12/src/main/java/com/google/apphosting/runtime/jetty/EE10AppEngineAuthentication.java index f502ba770..d20ce12d7 100644 --- a/shared_sdk_jetty12/src/main/java/com/google/apphosting/runtime/jetty/EE10AppEngineAuthentication.java +++ b/shared_sdk_jetty12/src/main/java/com/google/apphosting/runtime/jetty/EE10AppEngineAuthentication.java @@ -21,24 +21,21 @@ import com.google.appengine.api.users.UserServiceFactory; import com.google.apphosting.api.ApiProxy; import com.google.common.flogger.GoogleLogger; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; import java.security.Principal; import java.util.Arrays; import java.util.HashSet; import java.util.function.Function; import javax.security.auth.Subject; -import org.eclipse.jetty.ee10.servlet.ServletContextRequest; import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.security.AuthenticationState; import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.Constraint; import org.eclipse.jetty.security.DefaultIdentityService; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.LoginService; -import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.security.UserIdentity; import org.eclipse.jetty.security.authentication.LoginAuthenticator; import org.eclipse.jetty.server.Request; @@ -157,7 +154,7 @@ public Constraint.Authorization getConstraintAuthentication( * j.c.g.apphosting.utils.jetty.AppEngineAuthentication.AppEngineAuthenticator.authenticate(). * *

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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT jar 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.appengine parent - 2.0.28-SNAPSHOT + 2.0.29-SNAPSHOT true