This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new 6386e07743 Allow applications to trigger sending of 103 early hints 
(#764)
6386e07743 is described below

commit 6386e07743ac62a5d9acbe1dff342a60a4323dba
Author: Christopher Schultz <ch...@christopherschultz.net>
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   | 40 +++++++++--
 .../apache/catalina/connector/ResponseFacade.java  | 36 ++++++++--
 .../apache/coyote/http11/TestHttp11Processor.java  | 81 +++++++++++++++++++++-
 webapps/docs/changelog.xml                         |  5 ++
 4 files changed, 150 insertions(+), 12 deletions(-)

diff --git a/java/org/apache/catalina/connector/Response.java 
b/java/org/apache/catalina/connector/Response.java
index 66dd7cab47..a5066f915d 100644
--- a/java/org/apache/catalina/connector/Response.java
+++ b/java/org/apache/catalina/connector/Response.java
@@ -73,6 +73,8 @@ public class Response implements HttpServletResponse {
 
     private static final MediaTypeCache MEDIA_TYPE_CACHE = new 
MediaTypeCache(100);
 
+    protected static final int SC_EARLY_HINTS = 103;
+
     /**
      * Coyote response.
      */
@@ -1051,12 +1053,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 {
 
@@ -1069,16 +1091,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 dcc4ee059d..51f6a8e212 100644
--- a/java/org/apache/catalina/connector/ResponseFacade.java
+++ b/java/org/apache/catalina/connector/ResponseFacade.java
@@ -249,19 +249,47 @@ 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 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 fed616435b..0d8a9d766c 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(StandardCharsets.UTF_8);
             resp.setContentType("text/plain");
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 1438e45dc5..4b2881c9a7 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -246,6 +246,11 @@
         as a number of libraries and JRE features depend on this being non-null
         even when a SecurityManager is not is use. (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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to