Skip to content

Commit

Permalink
convert the JettyHttpProxy to use Jetty core instead of ee8
Browse files Browse the repository at this point in the history
Signed-off-by: Lachlan Roberts <[email protected]>
  • Loading branch information
lachlan-roberts committed Aug 4, 2023
1 parent 332d27c commit d3bd88f
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,19 @@
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.MessageLite;
import org.eclipse.jetty.ee8.nested.AbstractHandler;
import org.eclipse.jetty.ee8.nested.ContextHandler;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.util.Callback;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.util.Map;
Expand Down Expand Up @@ -120,12 +118,8 @@ public static Server newServer(ServletEngineAdapter.Config runtimeOptions, Forwa
config.setSendServerVersion(false);
config.setSendXPoweredBy(false);

// Wrap the ee8 Forwarding Handler as a Core Handler.
ContextHandler handler = new ContextHandler("/");
handler.setHandler(forwardingHandler);

CoreSizeLimitHandler sizeLimitHandler = new CoreSizeLimitHandler(MAX_REQUEST_SIZE, -1);
sizeLimitHandler.setHandler(handler);
sizeLimitHandler.setHandler(forwardingHandler);

GzipHandler gzip = new GzipHandler();
gzip.setInflateBufferSize(8 * 1024);
Expand Down Expand Up @@ -193,7 +187,7 @@ public long getStartTimeMillis() {
* app into it, and then forward HTTP requests over gRPC to the runtime and decode the responses.
*/
// The class has to be public, as it is a Servlet that needs to be loaded by the Jetty server.
public static class ForwardingHandler extends AbstractHandler {
public static class ForwardingHandler extends Handler.Abstract {

private static final String X_APPENGINE_TIMEOUT_MS = "x-appengine-timeout-ms";

Expand Down Expand Up @@ -231,24 +225,24 @@ private void init() {
}

/**
* Forwards a request to the real runtime for handling. We translate the javax.servlet types
* Forwards a request to the real runtime for handling. We translate the {@link Request} types
* into protocol buffers and send the request, then translate the response proto back to a
* HttpServletResponse.
* {@link Response}.
*/
@Override
public void handle(String target, org.eclipse.jetty.ee8.nested.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
baseRequest.setHandled(true);

public boolean handle(Request request, Response response, Callback callback) throws Exception {
// build the request object
RuntimePb.UPRequest upRequest = upRequestTranslator.translateRequest(baseRequest);
RuntimePb.UPRequest upRequest = upRequestTranslator.translateRequest(request);

try {
UPResponse upResponse = getUpResponse(upRequest);
upRequestTranslator.translateResponse(baseRequest.getResponse(), upResponse);
upRequestTranslator.translateResponse(response, upResponse, callback);
} catch (Throwable t) {
UPRequestTranslator.populateErrorResponse(
response, "Can't make request of app: " + Throwables.getStackTraceAsString(t));
String errorMsg = "Can't make request of app: " + Throwables.getStackTraceAsString(t);
UPRequestTranslator.populateErrorResponse(response, errorMsg, callback);
}

return true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@
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 org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Blocker;
import org.eclipse.jetty.util.Callback;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.ee8.nested.Request;
import org.eclipse.jetty.ee8.nested.Response;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;

/** Translates HttpServletRequest to the UPRequest proto, and vice versa for the response. */
public class UPRequestTranslator {
Expand Down Expand Up @@ -137,33 +143,29 @@ public UPRequestTranslator(
* @param response the Jetty response object to fill
* @param rpcResp the proto info available to extract info from
*/
public final void translateResponse(Response response, RuntimePb.UPResponse rpcResp) {
public final void translateResponse(Response response, RuntimePb.UPResponse rpcResp, Callback callback) {
HttpPb.HttpResponse rpcHttpResp = rpcResp.getHttpResponse();

if (rpcResp.getError() != RuntimePb.UPResponse.ERROR.OK.getNumber()) {
populateErrorResponse(response, "Request failed: " + rpcResp.getErrorMessage());
populateErrorResponse(response, "Request failed: " + rpcResp.getErrorMessage(), callback);
return;
}
response.setStatus(rpcHttpResp.getResponsecode());
for (HttpPb.ParsedHttpHeader header : rpcHttpResp.getOutputHeadersList()) {
response.addHeader(header.getKey(), header.getValue());
response.getHeaders().add(header.getKey(), header.getValue());
}

try {
response.getHttpOutput().sendContent(rpcHttpResp.getResponse().asReadOnlyByteBuffer());
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
response.write(true, rpcHttpResp.getResponse().asReadOnlyByteBuffer(), callback);
}

/**
* Makes a UPRequest from an HttpServletRequest
* Makes a UPRequest from a Jetty {@link Request}.
*
* @param realRequest the http request object
* @param jettyRequest the http request object
* @return equivalent UPRequest object
*/
@SuppressWarnings("JdkObsolete")
public final RuntimePb.UPRequest translateRequest(HttpServletRequest realRequest) {
public final RuntimePb.UPRequest translateRequest(Request jettyRequest) {
UPRequest.Builder upReqBuilder =
UPRequest.newBuilder()
.setAppId(appInfoFactory.getGaeApplication())
Expand All @@ -180,17 +182,10 @@ public final RuntimePb.UPRequest translateRequest(HttpServletRequest realRequest

upReqBuilder.setSecurityTicket(DEFAULT_SECRET_KEY);
upReqBuilder.setNickname("");
if (realRequest instanceof Request) {
// user efficient header iteration
for (HttpField field : ((Request) realRequest).getHttpFields()) {
builderHeader(upReqBuilder, field.getName(), field.getValue());
}
} else {
// slower iteration used for test case fake request only
for (String name : Collections.list(realRequest.getHeaderNames())) {
String value = realRequest.getHeader(name);
builderHeader(upReqBuilder, name, value);
}

// user efficient header iteration
for (HttpField field : jettyRequest.getHeaders()) {
builderHeader(upReqBuilder, field.getName(), field.getValue());
}

AppinfoPb.Handler handler =
Expand All @@ -205,37 +200,31 @@ public final RuntimePb.UPRequest translateRequest(HttpServletRequest realRequest
HttpPb.HttpRequest.Builder httpRequest =
upReqBuilder
.getRequestBuilder()
.setHttpVersion(realRequest.getProtocol())
.setProtocol(realRequest.getMethod())
.setUrl(getUrl(realRequest))
.setUserIp(realRequest.getRemoteAddr());

if (realRequest instanceof Request) {
// user efficient header iteration
for (HttpField field : ((Request) realRequest).getHttpFields()) {
requestHeader(upReqBuilder, httpRequest, field.getName(), field.getValue());
}
} else {
// slower iteration used for test case fake request only
for (String name : Collections.list(realRequest.getHeaderNames())) {
String value = realRequest.getHeader(name);
requestHeader(upReqBuilder, httpRequest, name, value);
}
.setHttpVersion(jettyRequest.getConnectionMetaData().getHttpVersion().asString())
.setProtocol(jettyRequest.getMethod())
.setUrl(getUrl(jettyRequest))
.setUserIp(Request.getRemoteAddr(jettyRequest));

// user efficient header iteration
for (HttpField field : jettyRequest.getHeaders()) {
requestHeader(upReqBuilder, httpRequest, field.getName(), field.getValue());
}

if (!skipPostData) {
try {
httpRequest.setPostdata(ByteString.readFrom(realRequest.getInputStream()));
InputStream inputStream = Content.Source.asInputStream(jettyRequest);
httpRequest.setPostdata(ByteString.readFrom(inputStream));
} catch (IOException ex) {
throw new IllegalStateException("Could not read POST content:", ex);
}
}

if ("/_ah/background".equals(realRequest.getRequestURI())) {
String decodedPath = jettyRequest.getHttpURI().getDecodedPath();
if ("/_ah/background".equals(decodedPath)) {
if (WARMUP_IP.equals(httpRequest.getUserIp())) {
upReqBuilder.setRequestType(UPRequest.RequestType.BACKGROUND);
}
} else if ("/_ah/start".equals(realRequest.getRequestURI())) {
} else if ("/_ah/start".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.
Expand Down Expand Up @@ -394,9 +383,10 @@ private void requestHeader(
}
}

private String getUrl(HttpServletRequest req) {
StringBuffer url = req.getRequestURL();
String query = req.getQueryString();
private String getUrl(Request req) {
HttpURI httpURI = req.getHttpURI();
StringBuilder url = new StringBuilder(HttpURI.build(httpURI).query(null).asString());
String query = httpURI.getQuery();
// No need to escape, URL retains any %-escaping it might have, which is what we want.
if (query != null) {
url.append('?').append(query);
Expand All @@ -410,14 +400,16 @@ private String getUrl(HttpServletRequest req) {
* @param resp response message to fill with info
* @param errMsg error text.
*/
public static void populateErrorResponse(HttpServletResponse resp, String errMsg) {
public static void populateErrorResponse(Response resp, String errMsg, Callback callback) {
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
try {
ServletOutputStream outstr = resp.getOutputStream();
outstr.print("<html><head><title>Server Error</title></head>");
outstr.print("<body>" + HtmlEscapers.htmlEscaper().escape(errMsg) + "</body></html>");
} catch (IOException iox) {
throw new IllegalStateException(iox);
try (OutputStream outstr = Content.Sink.asOutputStream(resp)) {
PrintWriter writer = new PrintWriter(outstr);
writer.print("<html><head><title>Server Error</title></head>");
writer.print("<body>" + HtmlEscapers.htmlEscaper().escape(errMsg) + "</body></html>");
writer.close();
callback.succeeded();
} catch (Throwable t) {
callback.failed(t);
}
}

Expand Down
Loading

0 comments on commit d3bd88f

Please sign in to comment.