On Tue, May 24, 2022 at 6:46 PM <[email protected]> wrote:
>
> 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 680db4448d Additional fixes for 66076
> 680db4448d is described below
>
> commit 680db4448d392752fb57b91639e7ab34a3f58105
> Author: Mark Thomas <[email protected]>
> AuthorDate: Tue May 24 17:46:20 2022 +0100
>
> Additional fixes for 66076
>
> The vectored IO version of the fix.
> ---
> java/org/apache/tomcat/util/net/AprEndpoint.java | 7 +++++++
> java/org/apache/tomcat/util/net/Nio2Endpoint.java | 7 +++++++
> java/org/apache/tomcat/util/net/NioEndpoint.java | 19
> ++++++++++++-------
> .../org/apache/tomcat/util/net/SocketWrapperBase.java | 4 +++-
> 4 files changed, 29 insertions(+), 8 deletions(-)
>
> diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java
> b/java/org/apache/tomcat/util/net/AprEndpoint.java
> index c02e90fc09..20d10efa11 100644
> --- a/java/org/apache/tomcat/util/net/AprEndpoint.java
> +++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
> @@ -2813,6 +2813,13 @@ public class AprEndpoint extends
> AbstractEndpoint<Long,Long> implements SNICallB
> return inline;
> }
>
> + @Override
> + protected boolean hasOutboundRemaining() {
> + // NIO2 never has remaining outbound data when the completion
> + // handler is called
> + return false;
> + }
> +
> @Override
> public void run() {
> // Perform the IO operation
> diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
> b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
> index 49ee411016..c5b9a395f5 100644
> --- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
> +++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
> @@ -1021,6 +1021,13 @@ public class Nio2Endpoint extends
> AbstractJsseEndpoint<Nio2Channel,AsynchronousS
> return Nio2Endpoint.isInline();
> }
>
> + @Override
> + protected boolean hasOutboundRemaining() {
> + // NIO2 never has remaining outbound data when the completion
> + // handler is called
> + return false;
> + }
> +
> @Override
> protected void start() {
> if (read) {
> diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java
> b/java/org/apache/tomcat/util/net/NioEndpoint.java
> index b8b6d4339d..ea65dd6600 100644
> --- a/java/org/apache/tomcat/util/net/NioEndpoint.java
> +++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
> @@ -1622,6 +1622,11 @@ public class NioEndpoint extends
> AbstractJsseEndpoint<NioChannel,SocketChannel>
> return inline;
> }
>
> + @Override
> + protected boolean hasOutboundRemaining() {
> + return getSocket().getOutboundRemaining() > 0;
> + }
> +
> @Override
> public void run() {
> // Perform the IO operation
> @@ -1654,13 +1659,13 @@ public class NioEndpoint extends
> AbstractJsseEndpoint<NioChannel,SocketChannel>
> } else {
> boolean doWrite = true;
> // Write from main buffer first
> - if
> (!socketBufferHandler.isWriteBufferEmpty()) {
> + if (socketOrNetworkBufferHasDataLeft()) {
> // There is still data inside the main
> write buffer, it needs to be written first
>
> socketBufferHandler.configureWriteBufferForRead();
> do {
> nBytes =
> getSocket().write(socketBufferHandler.getWriteBuffer());
> - } while
> (!socketBufferHandler.isWriteBufferEmpty() && nBytes > 0);
> - if
> (!socketBufferHandler.isWriteBufferEmpty()) {
> + } while
> (socketOrNetworkBufferHasDataLeft() && nBytes > 0);
> + if (socketOrNetworkBufferHasDataLeft()) {
> doWrite = false;
> }
> // Preserve a negative value since it is
> an error
> @@ -1681,7 +1686,8 @@ public class NioEndpoint extends
> AbstractJsseEndpoint<NioChannel,SocketChannel>
> updateLastWrite();
> }
> }
> - if (nBytes != 0 ||
> !buffersArrayHasRemaining(buffers, offset, length)) {
> + if (nBytes != 0 ||
> (!buffersArrayHasRemaining(buffers, offset, length) &&
> + !socketOrNetworkBufferHasDataLeft())) {
> completionDone = false;
> }
> }
> @@ -1689,7 +1695,8 @@ public class NioEndpoint extends
> AbstractJsseEndpoint<NioChannel,SocketChannel>
> setError(e);
> }
> }
> - if (nBytes > 0 || (nBytes == 0 &&
> !buffersArrayHasRemaining(buffers, offset, length))) {
> + if (nBytes > 0 || (nBytes == 0 &&
> !buffersArrayHasRemaining(buffers, offset, length) &&
> + !socketOrNetworkBufferHasDataLeft())) {
> // The bytes processed are only updated in the
> completion handler
> completion.completed(Long.valueOf(nBytes), this);
> } else if (nBytes < 0 || getError() != null) {
> @@ -1708,9 +1715,7 @@ public class NioEndpoint extends
> AbstractJsseEndpoint<NioChannel,SocketChannel>
> }
> }
> }
> -
> }
> -
> }
>
>
> diff --git a/java/org/apache/tomcat/util/net/SocketWrapperBase.java
> b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
> index bcb0119fc6..d95a31a15d 100644
> --- a/java/org/apache/tomcat/util/net/SocketWrapperBase.java
> +++ b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
> @@ -1036,6 +1036,8 @@ public abstract class SocketWrapperBase<E> {
> */
> protected abstract boolean isInline();
>
> + protected abstract boolean hasOutboundRemaining();
> +
> /**
> * Process the operation using the connector executor.
> * @return true if the operation was accepted, false if the executor
> @@ -1087,7 +1089,7 @@ public abstract class SocketWrapperBase<E> {
> boolean completion = true;
> if (state.check != null) {
> CompletionHandlerCall call =
> state.check.callHandler(currentState, state.buffers, state.offset,
> state.length);
> - if (call == CompletionHandlerCall.CONTINUE) {
> + if (call == CompletionHandlerCall.CONTINUE ||
> state.hasOutboundRemaining()) {
I understand why it's there (the operation will indeed seem complete
in that case, but it's not fully complete), but it needs to check
state.read then (looping a read operation will most likely not do
anything good; maybe it shouldn't happen, but it's a lot better to be
safe), so:
if (call == CompletionHandlerCall.CONTINUE || (!state.read &&
state.hasOutboundRemaining())) {
Overall the change is more convoluted than I expected, and I would
have messed it up without a test case.
Rémy
> complete = false;
> } else if (call == CompletionHandlerCall.NONE) {
> completion = false;
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]