This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push: new 739c7381ae Fix 66591 - Make sure every response includes a Send Headers packet 739c7381ae is described below commit 739c7381aed22b7636351caf885ddc519ab6b442 Author: Mark Thomas <ma...@apache.org> AuthorDate: Wed May 3 17:05:48 2023 +0100 Fix 66591 - Make sure every response includes a Send Headers packet --- java/org/apache/coyote/ajp/AjpProcessor.java | 74 ++++++++++++---------- .../coyote/ajp/TestAbstractAjpProcessor.java | 56 +++++++++++++++- webapps/docs/changelog.xml | 5 ++ 3 files changed, 99 insertions(+), 36 deletions(-) diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java index 93d671cb7f..ccf2e78ba1 100644 --- a/java/org/apache/coyote/ajp/AjpProcessor.java +++ b/java/org/apache/coyote/ajp/AjpProcessor.java @@ -956,43 +956,47 @@ public class AjpProcessor extends AbstractProcessor { responseMsgPos = -1; int numHeaders = headers.size(); - for (int i = 0; i < numHeaders; i++) { - if (i == 0) { - // Write AJP message header - responseMessage.reset(); - responseMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS); - - // Write HTTP response line - responseMessage.appendInt(statusCode); - // Reason phrase is optional but mod_jk + httpd 2.x fails with a null - // reason phrase - bug 45026 - tmpMB.setString(Integer.toString(response.getStatus())); - responseMessage.appendBytes(tmpMB); - - // Start headers - responseMessage.appendInt(numHeaders); - } + boolean needAjpMessageHeader = true; + while (needAjpMessageHeader) { + // Write AJP message header + responseMessage.reset(); + responseMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS); - try { - // Write headers - MessageBytes hN = headers.getName(i); - int hC = Constants.getResponseAjpIndex(hN.toString()); - if (hC > 0) { - responseMessage.appendInt(hC); - } else { - responseMessage.appendBytes(hN); + // Write HTTP response line + responseMessage.appendInt(statusCode); + // Reason phrase is optional but mod_jk + httpd 2.x fails with a null + // reason phrase - bug 45026 + tmpMB.setString(Integer.toString(response.getStatus())); + responseMessage.appendBytes(tmpMB); + + // Start headers + responseMessage.appendInt(numHeaders); + + needAjpMessageHeader = false; + + for (int i = 0; i < numHeaders; i++) { + try { + // Write headers + MessageBytes hN = headers.getName(i); + int hC = Constants.getResponseAjpIndex(hN.toString()); + if (hC > 0) { + responseMessage.appendInt(hC); + } else { + responseMessage.appendBytes(hN); + } + MessageBytes hV = headers.getValue(i); + responseMessage.appendBytes(hV); + } catch (IllegalArgumentException iae) { + // Log the problematic header + log.warn(sm.getString("ajpprocessor.response.invalidHeader", headers.getName(i), headers.getValue(i)), + iae); + // Remove the problematic header + headers.removeHeader(i); + numHeaders--; + // Restart writing of AJP message + needAjpMessageHeader = true; + break; } - MessageBytes hV = headers.getValue(i); - responseMessage.appendBytes(hV); - } catch (IllegalArgumentException iae) { - // Log the problematic header - log.warn(sm.getString("ajpprocessor.response.invalidHeader", headers.getName(i), headers.getValue(i)), - iae); - // Remove the problematic header - headers.removeHeader(i); - numHeaders--; - // Reset loop and start again - i = -1; } } diff --git a/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java b/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java index 111b033e85..22b0466e6f 100644 --- a/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java +++ b/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java @@ -883,6 +883,60 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest { } + /* + * https://bz.apache.org/bugzilla/show_bug.cgi?id=66591 + */ + @Test + public void testNoHeaders() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + Tomcat.addServlet(ctx, "bug66591", new NoHeadersServlet()); + ctx.addServletMappingDecoded("/", "bug66591"); + + tomcat.start(); + + SimpleAjpClient ajpClient = new SimpleAjpClient(); + ajpClient.setPort(getPort()); + ajpClient.connect(); + + validateCpong(ajpClient.cping()); + + TesterAjpMessage forwardMessage = ajpClient.createForwardMessage(); + forwardMessage.end(); + + TesterAjpMessage responseHeaderMessage = ajpClient.sendMessage(forwardMessage, null); + + // Expect 3 messages: headers, body chunk, end + Map<String, List<String>> responseHeaders = validateResponseHeaders(responseHeaderMessage, 200, "200"); + Assert.assertTrue(responseHeaders.isEmpty()); + + String body = extractResponseBody(ajpClient.readMessage()); + Assert.assertTrue(body.isEmpty()); + + validateResponseEnd(ajpClient.readMessage(), true); + + // Double check the connection is still open + validateCpong(ajpClient.cping()); + + ajpClient.disconnect(); + } + + + private static class NoHeadersServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.flushBuffer(); + } + } + + /** * Process response header packet and checks the status. Any other data is ignored. */ @@ -943,7 +997,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest { Assert.assertEquals(0x03, message.readByte()); int len = message.readInt(); - Assert.assertTrue(len > 0); + Assert.assertTrue(len >= 0); return message.readString(len); } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 0de19ecbcd..89d263c6cf 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -152,6 +152,11 @@ <code>allowHostHeaderMismatch</code>. These are now hard-coded to the previous defaults. (markt) </update> + <fix> + <bug>66591</bug>: Fix a regression introduced in the fix for + <bug>66512</bug> that meant that an AJP Send Headers was not sent for + responses where no HTTP headers were set. (markt) + </fix> </changelog> </subsection> <subsection name="Jasper"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org