This is an automated email from the ASF dual-hosted git repository.
markt-asf 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 dea4e93a7a Align flushing buffered writes with HTTP.
dea4e93a7a is described below
commit dea4e93a7a12e2f5f98f48a953905ced7816a1ac
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Sep 29 16:50:08 2026 +0100
Align flushing buffered writes with HTTP.
- removes the responseMsgPos field as it was unused. Large non-blocking
writes are written fully to one or more AJP messages and then
buffered at socketWrapper as required
- the write registration at the response layer should not be updated
while the socket wrapper buffer is being emptied
- reformat the code
---
java/org/apache/coyote/ajp/AjpProcessor.java | 49 +++++++++++++---------------
webapps/docs/changelog.xml | 4 +++
2 files changed, 26 insertions(+), 27 deletions(-)
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java
b/java/org/apache/coyote/ajp/AjpProcessor.java
index 25d341d458..8f8f45abe6 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -177,13 +177,6 @@ public class AjpProcessor extends AbstractProcessor {
private final AjpMessage responseMessage;
- /**
- * Intended to hold the location of the next write of the response message
when non-blocking writes do not write
- * the message in a single write. Always -1 in the current implementation
as the write path does not update it.
- */
- private int responseMsgPos = -1;
-
-
/**
* Body message.
*/
@@ -262,7 +255,7 @@ public class AjpProcessor extends AbstractProcessor {
* Constructs a new AjpProcessor.
*
* @param protocol The AJP protocol
- * @param adapter The adapter for this processor
+ * @param adapter The adapter for this processor
*/
public AjpProcessor(AbstractAjpProtocol<?> protocol, Adapter adapter) {
super(adapter);
@@ -297,12 +290,16 @@ public class AjpProcessor extends AbstractProcessor {
@Override
protected boolean flushBufferedWrite() throws IOException {
- if (hasDataToWrite()) {
+ if (socketWrapper.hasDataToWrite()) {
socketWrapper.flush(false);
- if (hasDataToWrite()) {
- // There is data to write but go via Response to
- // maintain a consistent view of non-blocking state
- response.checkRegisterForWrite();
+ if (socketWrapper.hasDataToWrite()) {
+ /*
+ * The socketWrapper wasn't fully flushed so re-register the
socket for write. Note this does not go via
+ * the Response since the write registration state at that
level should remain unchanged. Once the
+ * socketWrapper has been emptied then the registration below
will trigger a call to
+ * Adaptor.asyncDispatch() which will enable the Response to
respond to this event.
+ */
+ socketWrapper.registerWriteInterest();
return true;
}
}
@@ -650,7 +647,8 @@ public class AjpProcessor extends AbstractProcessor {
if (methodCode != Constants.SC_M_JK_STORED) {
String methodName = Constants.getMethodForCode(methodCode - 1);
if (methodName == null) {
- throw new
IllegalArgumentException(sm.getString("ajpprocessor.request.invalidMethod",
String.valueOf(methodCode)));
+ throw new IllegalArgumentException(
+ sm.getString("ajpprocessor.request.invalidMethod",
String.valueOf(methodCode)));
}
request.setMethod(methodName);
}
@@ -695,7 +693,8 @@ public class AjpProcessor extends AbstractProcessor {
requestHeaderMessage.getInt(); // To advance the read position
hName = Constants.getHeaderForCode(hId - 1);
if (hName == null) {
- throw new
IllegalArgumentException(sm.getString("ajpprocessor.request.invalidHeader",
String.valueOf(hId)));
+ throw new IllegalArgumentException(
+ sm.getString("ajpprocessor.request.invalidHeader",
String.valueOf(hId)));
}
vMB = headers.addValue(hName);
} else {
@@ -813,9 +812,10 @@ public class AjpProcessor extends AbstractProcessor {
case Constants.SC_A_JVM_ROUTE ->
requestHeaderMessage.getBytes(tmpMB);
// nothing
- case Constants.SC_A_SSL_CERT ->
- // SSL certificate extraction is lazy, moved to
JkCoyoteHandler
- requestHeaderMessage.getBytes(certificates);
+ case Constants.SC_A_SSL_CERT -> {
+ // SSL certificate extraction is lazy, moved to
JkCoyoteHandler
+ requestHeaderMessage.getBytes(certificates);
+ }
case Constants.SC_A_SSL_CIPHER -> {
requestHeaderMessage.getBytes(tmpMB);
request.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
tmpMB.toString());
@@ -824,8 +824,9 @@ public class AjpProcessor extends AbstractProcessor {
requestHeaderMessage.getBytes(tmpMB);
request.setAttribute(SSLSupport.SESSION_ID_KEY,
tmpMB.toString());
}
- case Constants.SC_A_SSL_KEY_SIZE ->
- request.setAttribute(SSLSupport.KEY_SIZE_KEY,
Integer.valueOf(requestHeaderMessage.getInt()));
+ case Constants.SC_A_SSL_KEY_SIZE -> {
+ request.setAttribute(SSLSupport.KEY_SIZE_KEY,
Integer.valueOf(requestHeaderMessage.getInt()));
+ }
case Constants.SC_A_STORED_METHOD -> {
requestHeaderMessage.getBytes(tmpMB);
ByteChunk tmpBC = tmpMB.getByteChunk();
@@ -945,7 +946,6 @@ public class AjpProcessor extends AbstractProcessor {
}
tmpMB.recycle();
- responseMsgPos = -1;
int numHeaders = headers.size();
boolean needAjpMessageHeader = true;
@@ -1201,7 +1201,7 @@ public class AjpProcessor extends AbstractProcessor {
@Override
protected final boolean isReadyForWrite() {
- return responseMsgPos == -1 && socketWrapper.isReadyForWrite();
+ return socketWrapper.isReadyForWrite();
}
@@ -1274,11 +1274,6 @@ public class AjpProcessor extends AbstractProcessor {
}
- private boolean hasDataToWrite() {
- return responseMsgPos != -1 || socketWrapper.hasDataToWrite();
- }
-
-
@Override
protected Log getLog() {
return log;
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index cfb126a8bb..7f82af3f43 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -335,6 +335,10 @@
Skip the write notification in these cases since it would be useless.
(remm)
</fix>
+ <fix>
+ Fix a timing issue that could cause large non-blocking writes to stall
+ for AJP connections. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]