diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java index 6553c034af..99e0291fec 100644 --- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java +++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java @@ -46,6 +46,7 @@ import javax.net.ssl.SSLSession; import org.apache.hc.core5.concurrent.CancellableDependency; +import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.Header; @@ -65,6 +66,7 @@ import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.ExecutableCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; +import org.apache.hc.core5.http.nio.command.StaleCheckCommand; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; @@ -410,6 +412,11 @@ private int generateStreamId() { } } + void doStalecheck(final FutureCallback callback) throws IOException { + callback.completed( + ioSession.isOpen() && connState.compareTo(ConnectionHandshake.ACTIVE) == 0); + } + public final void onConnect() throws HttpException, IOException { connState = ConnectionHandshake.ACTIVE; final RawFrame settingsFrame = frameFactory.createSettings( @@ -647,6 +654,8 @@ private void processPendingCommands() throws IOException, HttpException { if (!outputQueue.isEmpty()) { return; } + } else if (command instanceof StaleCheckCommand) { + doStalecheck(((StaleCheckCommand) command).getCallback()); } } } diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java index b1581e9995..b496eb2e3f 100644 --- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java +++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java @@ -39,9 +39,8 @@ import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.nio.command.ShutdownCommand; +import org.apache.hc.core5.http.nio.command.StaleCheckCommand; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; -import org.apache.hc.core5.http2.nio.command.PingCommand; -import org.apache.hc.core5.http2.nio.support.BasicPingHandler; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.AbstractIOSessionPool; import org.apache.hc.core5.reactor.Command; @@ -146,11 +145,24 @@ protected void validateSession( final long lastAccessTime = Math.min(ioSession.getLastReadTime(), ioSession.getLastWriteTime()); final long deadline = lastAccessTime + timeValue.toMilliseconds(); if (deadline <= System.currentTimeMillis()) { - final Timeout socketTimeoutMillis = ioSession.getSocketTimeout(); - ioSession.enqueue(new PingCommand(new BasicPingHandler(result -> { - ioSession.setSocketTimeout(socketTimeoutMillis); - callback.execute(result); - })), Command.Priority.NORMAL); + ioSession.enqueue(new StaleCheckCommand(new FutureCallback() { + + @Override + public void completed(final Boolean result) { + callback.execute(result); + } + + @Override + public void failed(final Exception ex) { + callback.execute(false); + } + + @Override + public void cancelled() { + callback.execute(false); + } + + }), Command.Priority.NORMAL); return; } } diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java index 8cd4915246..a46504bd3f 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java @@ -40,6 +40,7 @@ import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; +import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.EndpointDetails; @@ -68,6 +69,7 @@ import org.apache.hc.core5.http.nio.command.CommandSupport; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; +import org.apache.hc.core5.http.nio.command.StaleCheckCommand; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.SocketTimeoutExceptionFactory; import org.apache.hc.core5.reactor.Command; @@ -244,6 +246,8 @@ private void processCommands() throws HttpException, IOException { execute((RequestExecutionCommand) command); return; } + } else if (command instanceof StaleCheckCommand) { + doStalecheck(((StaleCheckCommand) command).getCallback()); } else { throw new HttpException("Unexpected command: " + command.getClass()); } @@ -429,6 +433,11 @@ void requestShutdown(final CloseMode closeMode) { ioSession.setEvent(SelectionKey.OP_WRITE); } + void doStalecheck(final FutureCallback callback) throws IOException { + callback.completed( + ioSession.isOpen() && connState.compareTo(ConnectionState.ACTIVE) == 0); + } + void commitMessageHead( final OutgoingMessage messageHead, final boolean endStream, diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/StaleCheckCommand.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/StaleCheckCommand.java new file mode 100644 index 0000000000..d5ba15b3e0 --- /dev/null +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/StaleCheckCommand.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.hc.core5.http.nio.command; + +import org.apache.hc.core5.annotation.Internal; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.reactor.Command; +import org.apache.hc.core5.util.Args; + +/** + * Stale check command. The {@code callback} will be invoked after the client's event loop has finished processing + * all pending reads on the connection. If the connection is still active at that point, the callback will be completed + * with {@code true}. In any other event, the callback will be cancelled, failed, or completed with {@code false}. + * + * @since 5.4 + */ +@Internal +public final class StaleCheckCommand implements Command { + + private final FutureCallback callback; + + public StaleCheckCommand(final FutureCallback callback) { + this.callback = Args.notNull(callback, "Callback"); + } + + public FutureCallback getCallback() { + return callback; + } + + @Override + public boolean cancel() { + callback.cancelled(); + return true; + } + +}