Skip to content

Commit 7f3e6d6

Browse files
committed
servlet: fix write when not ready in AsyncServletOutputStreamWriter
Even when isReady() returns true, the servlet container may still be processing a previous write. Always set readyAndDrained to false after a direct write to ensure subsequent writes go through the container thread via onWritePossible(), matching servlet spec. Fixes #12723
1 parent f430131 commit 7f3e6d6

1 file changed

Lines changed: 9 additions & 7 deletions

File tree

servlet/src/main/java/io/grpc/servlet/AsyncServletOutputStreamWriter.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,15 @@ private void runOrBuffer(ActionItem actionItem) throws IOException {
222222
if (actionItem == completeAction) {
223223
return;
224224
}
225-
if (!isReady.getAsBoolean()) {
226-
boolean successful =
227-
writeState.compareAndSet(curState, curState.withReadyAndDrained(false));
228-
LockSupport.unpark(parkingThread);
229-
checkState(successful, "Bug: curState is unexpectedly changed by another thread");
230-
log.finest("the servlet output stream becomes not ready");
231-
}
225+
// Even if isReady() returns true, the previous write may still be in progress
226+
// in the servlet container. Set readyAndDrained to false to require onWritePossible()
227+
// to re-enable direct writes. This prevents "write when not ready" errors from Tomcat
228+
// and other servlet containers that require onWritePossible() to fire between writes.
229+
boolean successful =
230+
writeState.compareAndSet(curState, curState.withReadyAndDrained(false));
231+
LockSupport.unpark(parkingThread);
232+
checkState(successful, "Bug: curState is unexpectedly changed by another thread");
233+
log.finest("the servlet output stream becomes not ready");
232234
} else { // buffer to the writeChain
233235
writeChain.offer(actionItem);
234236
if (!writeState.compareAndSet(curState, curState.withReadyAndDrained(false))) {

0 commit comments

Comments
 (0)