This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 9e767b24cd Allow applications to trigger sending of 103 early hints
(#764)
9e767b24cd is described below
commit 9e767b24cd8092964246f95ebe8e90cb8073e69d
Author: Christopher Schultz <[email protected]>
AuthorDate: Wed Nov 6 05:38:27 2024 -0500
Allow applications to trigger sending of 103 early hints (#764)
* Allow applications to trigger sending of 103 early hints
* Add unit tests
---
java/org/apache/catalina/connector/Response.java | 39 +++++++++--
.../apache/catalina/connector/ResponseFacade.java | 36 ++++++++--
.../apache/coyote/http11/TestHttp11Processor.java | 81 +++++++++++++++++++++-
webapps/docs/changelog.xml | 5 ++
4 files changed, 149 insertions(+), 12 deletions(-)
diff --git a/java/org/apache/catalina/connector/Response.java
b/java/org/apache/catalina/connector/Response.java
index 0e8fe8d2c5..c79bb3b5a4 100644
--- a/java/org/apache/catalina/connector/Response.java
+++ b/java/org/apache/catalina/connector/Response.java
@@ -91,6 +91,7 @@ public class Response implements HttpServletResponse {
System.getProperty("org.apache.catalina.connector.Response.ENFORCE_ENCODING_IN_GET_WRITER",
"true"));
}
+ protected static final int SC_EARLY_HINTS = 103;
// ----------------------------------------------------- Instance Variables
@@ -1137,12 +1138,32 @@ public class Response implements HttpServletResponse {
}
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Calling <code>sendError</code> with a status code of 103 differs from
the usual
+ * behavior. Sending 103 will trigger the container to send a "103 Early
Hints" informational response including all
+ * current headers. The application can continue to use the request and
response after calling sendError with a 103
+ * status code, including triggering a more typical response of any type.
+ * <p>
+ * Starting with Tomcat 12, applications should use {@link
#sendEarlyHints}.
+ */
@Override
public void sendError(int status) throws IOException {
sendError(status, null);
}
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Calling <code>sendError</code> with a status code of 103 differs from
the usual
+ * behavior. Sending 103 will trigger the container to send a "103 Early
Hints" informational response including all
+ * current headers. The application can continue to use the request and
response after calling sendError with a 103
+ * status code, including triggering a more typical response of any type.
+ * <p>
+ * Starting with Tomcat 12, applications should use {@link
#sendEarlyHints}.
+ */
@Override
public void sendError(int status, String message) throws IOException {
@@ -1155,16 +1176,20 @@ public class Response implements HttpServletResponse {
return;
}
- setError();
+ if (SC_EARLY_HINTS == status) {
+ sendEarlyHints();
+ } else {
+ setError();
- getCoyoteResponse().setStatus(status);
- getCoyoteResponse().setMessage(message);
+ getCoyoteResponse().setStatus(status);
+ getCoyoteResponse().setMessage(message);
- // Clear any data content that has been buffered
- resetBuffer();
+ // Clear any data content that has been buffered
+ resetBuffer();
- // Cause the response to be finished (from the application perspective)
- setSuspended(true);
+ // Cause the response to be finished (from the application
perspective)
+ setSuspended(true);
+ }
}
diff --git a/java/org/apache/catalina/connector/ResponseFacade.java
b/java/org/apache/catalina/connector/ResponseFacade.java
index 068c68982b..dc53b090c3 100644
--- a/java/org/apache/catalina/connector/ResponseFacade.java
+++ b/java/org/apache/catalina/connector/ResponseFacade.java
@@ -329,6 +329,16 @@ public class ResponseFacade implements HttpServletResponse
{
response.sendEarlyHints();
}
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Calling <code>sendError</code> with a status code of 103 differs from
the usual
+ * behavior. Sending 103 will trigger the container to send a "103 Early
Hints" informational response including all
+ * current headers. The application can continue to use the request and
response after calling sendError with a 103
+ * status code, including triggering a more typical response of any type.
+ * <p>
+ * Starting with Tomcat 12, applications should use {@link
#sendEarlyHints}.
+ */
@Override
public String encodeUrl(String url) {
checkFacade();
@@ -346,16 +356,34 @@ public class ResponseFacade implements
HttpServletResponse {
@Override
public void sendError(int sc, String msg) throws IOException {
checkCommitted("coyoteResponse.sendError.ise");
- response.setAppCommitted(true);
- response.sendError(sc, msg);
+ if (Response.SC_EARLY_HINTS == sc) {
+ sendEarlyHints();
+ } else {
+ response.setAppCommitted(true);
+ response.sendError(sc, msg);
+ }
}
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Calling <code>sendError</code> with a status code of 103 differs from
the usual
+ * behavior. Sending 103 will trigger the container to send a "103 Early
Hints" informational response including all
+ * current headers. The application can continue to use the request and
response after calling sendError with a 103
+ * status code, including triggering a more typical response of any type.
+ * <p>
+ * Starting with Tomcat 12, applications should use {@link
#sendEarlyHints}.
+ */
@Override
public void sendError(int sc) throws IOException {
checkCommitted("coyoteResponse.sendError.ise");
- response.setAppCommitted(true);
- response.sendError(sc);
+ if (Response.SC_EARLY_HINTS == sc) {
+ sendEarlyHints();
+ } else {
+ response.setAppCommitted(true);
+ response.sendError(sc);
+ }
}
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java
b/test/org/apache/coyote/http11/TestHttp11Processor.java
index 4d97532999..598ba88ef1 100644
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
@@ -1946,16 +1946,95 @@ public class TestHttp11Processor extends TomcatBaseTest
{
Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode());
}
+ @Test
+ public void testEarlyHintsSendError() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = getProgrammaticRootContext();
+
+ // Add servlet
+ Tomcat.addServlet(ctx, "EarlyHintsServlet", new
EarlyHintsServlet(true, null));
+ ctx.addServletMappingDecoded("/ehs", "EarlyHintsServlet");
+
+ tomcat.start();
+
+ String request = "GET /ehs HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(tomcat.getConnector().getLocalPort());
+ client.setRequest(new String[] { request });
+
+ client.connect(600000, 600000);
+ client.processRequest(false);
+
+ Assert.assertEquals(103, client.getStatusCode());
+
+ client.readResponse(false);
+ Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode());
+ }
+
+
+ @Test
+ public void testEarlyHintsSendErrorWithMessage() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = getProgrammaticRootContext();
+
+ // Add servlet
+ Tomcat.addServlet(ctx, "EarlyHintsServlet", new
EarlyHintsServlet(true, "ignored"));
+ ctx.addServletMappingDecoded("/ehs", "EarlyHintsServlet");
+
+ tomcat.start();
+
+ String request = "GET /ehs HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(tomcat.getConnector().getLocalPort());
+ client.setRequest(new String[] { request });
+
+ client.connect(600000, 600000);
+ client.processRequest(false);
+
+ Assert.assertEquals(103, client.getStatusCode());
+
+ client.readResponse(false);
+ Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode());
+ }
+
+
private static class EarlyHintsServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
+ private final boolean useSendError;
+ private final String errorString;
+
+ EarlyHintsServlet() {
+ this(false, null);
+ }
+
+ EarlyHintsServlet(boolean useSendError, String errorString) {
+ this.useSendError = useSendError;
+ this.errorString = errorString;
+ }
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.addHeader("Link", "</style.css>; rel=preload; as=style");
- ((ResponseFacade) resp).sendEarlyHints();
+ if (useSendError) {
+ if (null == errorString) {
+ resp.sendError(103);
+ } else {
+ resp.sendError(103, errorString);
+ }
+ } else {
+ ((ResponseFacade) resp).sendEarlyHints();
+ }
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/plain");
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index c378b8d2ae..8d77263b5a 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -240,6 +240,11 @@
levels of nested includes. Based on a patch provided by John
Engebretson. (markt)
</fix>
+ <add>
+ All applications to send an early hints informational response by
+ calling <code>HttpServletResponse.sendError()</code> with a status code
+ of 103. (schultz)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]