Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ public JettyRequestAPIData(

HttpFields.Mutable fields = HttpFields.build();
for (HttpField field : request.getHeaders()) {
// If it has a HttpHeader it is one of the standard headers so won't match any appengine specific header.
if (field.getHeader() != null) {
fields.add(field);
continue;
}
Comment on lines +122 to +126
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Actually even without this logic, a non-AE header should have been added by the line below no? Am I missing something here since the switch is only to certain certain flags and not to prevent addition of fields?
  2. If my above statement is wrong and that we still need to have this check, can we please this condition along with that line mentioned there? Just to have a more consistent flow of readability on what all fields are getting added?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct, it is still correct without this.

This is just an optimization so that we can skip the rest of the logic in the loop as we know straight away it will not match any of these headers.


String name = field.getLowerCaseName();
String value = field.getValue();
if (Strings.isNullOrEmpty(value)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpURI;
Expand Down Expand Up @@ -126,10 +128,15 @@ public JettyRequestAPIData(
this.securityTicket = DEFAULT_SECRET_KEY;

HttpFields fields = new HttpFields();
List<String> headerNames = Collections.list(request.getHeaderNames());
for (String headerName : headerNames) {
String name = headerName.toLowerCase(Locale.ROOT);
String value = request.getHeader(headerName);
for (HttpField field : request.getHttpFields()) {
// If it has a HttpHeader it is one of the standard headers so won't match any appengine specific header.
if (field.getHeader() != null) {
fields.add(field);
continue;
}

String name = field.getName().toLowerCase(Locale.ROOT);
String value = field.getValue();
if (Strings.isNullOrEmpty(value)) {
continue;
}
Expand Down Expand Up @@ -238,7 +245,7 @@ public JettyRequestAPIData(

if (passThroughPrivateHeaders || !PRIVATE_APPENGINE_HEADERS.contains(name)) {
// Only non AppEngine specific headers are passed to the application.
fields.add(name, value);
fields.add(field);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
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;
Expand Down Expand Up @@ -141,10 +140,10 @@ public void start(String serverInfo, ServletEngineAdapter.Config runtimeOptions)
if (Boolean.getBoolean(HTTP_CONNECTOR_MODE)) {
logger.atInfo().log("Using HTTP_CONNECTOR_MODE to bypass RPC");
appVersionKey = AppVersionKey.fromAppInfo(appinfo);
JettyHttpProxy.insertHandlers(server);
AppVersion appVersion = appVersionHandlerMap.getAppVersion(appVersionKey);
server.insertHandler(
new JettyHttpHandler(runtimeOptions, appVersion, appVersionKey, appInfoFactory));
JettyHttpProxy.insertHandlers(server);
ServerConnector connector = JettyHttpProxy.newConnector(server, runtimeOptions);
server.addConnector(connector);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* 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.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
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 GzipHandlerTest extends JavaRuntimeViaHttpBase {

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[][] {
{"jetty94", false},
{"jetty94", true},
{"ee8", false},
{"ee8", true},
{"ee10", false},
{"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 GzipHandlerTest(String environment, boolean httpMode) {
this.environment = environment;
this.httpMode = httpMode;
System.setProperty("appengine.use.HttpConnector", Boolean.toString(httpMode));
}

@Before
public void before() throws Exception {
String app = "com/google/apphosting/runtime/jetty9/gzipapp/" + environment;
copyAppToDir(app, temp.getRoot().toPath());
httpClient.start();
runtime = runtimeContext();
System.err.println("==== Using Environment: " + environment + " " + httpMode + " ====");
}

@After
public void after() throws Exception {
httpClient.stop();
runtime.close();
}

@Test
public void testRequestGzipContent() throws Exception {
int contentLength = 1024;

CompletableFuture<Result> 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);

// The request was successfully decoded by the GzipHandler.
Result response = completionListener.get(5, TimeUnit.SECONDS);
assertThat(response.getResponse().getStatus(), equalTo(HttpStatus.OK_200));
String contentReceived = received.toString();
assertThat(contentReceived, containsString("\nX-Content-Encoding: gzip\n"));
assertThat(contentReceived, not(containsString("\nContent-Encoding: gzip\n")));
assertThat(contentReceived, containsString("\nAccept-Encoding: gzip\n"));

// Server correctly echoed content of request.
String expectedData = new String(data);
String actualData = contentReceived.substring(contentReceived.length() - contentLength);
assertThat(actualData, equalTo(expectedData));

// Response was gzip encoded.
HttpFields responseHeaders = response.getResponse().getHeaders();
assertThat(responseHeaders.get(HttpHeader.CONTENT_ENCODING), equalTo("gzip"));
}

private RuntimeContext<?> runtimeContext() throws Exception {
RuntimeContext.Config<?> config =
RuntimeContext.Config.builder().setApplicationPath(temp.getRoot().toString()).build();
return RuntimeContext.create(config);
}

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());
}
}
4 changes: 4 additions & 0 deletions runtime/testapps/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.gzipapp;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import org.eclipse.jetty.util.IO;

/** Servlet that prints all the system properties. */
public class EE10EchoServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/plain");

PrintWriter writer = resp.getWriter();
writer.println();
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
writer.println(headerName + ": " + req.getHeader(headerName));
}
writer.println();

String string = IO.toString(req.getInputStream());
writer.print(string);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.gzipapp;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.IO;

/** Servlet that prints all the system properties. */
public class EE8EchoServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/plain");

PrintWriter writer = resp.getWriter();
writer.println();
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
writer.println(headerName + ": " + req.getHeader(headerName));
}
writer.println();

String string = IO.toString(req.getInputStream());
writer.print(string);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<runtime>java21</runtime>
<application>gzip</application>
<system-properties>
<property name="appengine.use.EE10" value="true"/>
</system-properties>
</appengine-web-app>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->

<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
metadata-complete="false"
version="3.1">
<servlet>
<servlet-name>Main</servlet-name>
<servlet-class>com.google.apphosting.runtime.jetty9.gzipapp.EE10EchoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Loading