Issue #2788 - Graceful close of HTTP/2 Connection.
Updates after review. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
916b3e56ec
commit
3a6c9b8049
|
@ -102,7 +102,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
private boolean connectProtocolEnabled;
|
private boolean connectProtocolEnabled;
|
||||||
private long idleTime;
|
private long idleTime;
|
||||||
private GoAwayFrame closeFrame;
|
private GoAwayFrame closeFrame;
|
||||||
private Callback.Completable closeCallback;
|
private Callback.Completable shutdownCallback;
|
||||||
|
|
||||||
public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId)
|
public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId)
|
||||||
{
|
{
|
||||||
|
@ -439,27 +439,17 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Received {}", frame);
|
LOG.debug("Received {}", frame);
|
||||||
|
|
||||||
while (true)
|
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.REMOTELY_CLOSED))
|
||||||
{
|
{
|
||||||
CloseState current = closed.get();
|
// We received a GO_AWAY, so try to write
|
||||||
if (current == CloseState.NOT_CLOSED)
|
// what's in the queue and then disconnect.
|
||||||
{
|
closeFrame = frame;
|
||||||
if (closed.compareAndSet(current, CloseState.REMOTELY_CLOSED))
|
notifyClose(this, frame, new DisconnectCallback());
|
||||||
{
|
return;
|
||||||
// We received a GO_AWAY, so try to write
|
|
||||||
// what's in the queue and then disconnect.
|
|
||||||
closeFrame = frame;
|
|
||||||
notifyClose(this, frame, new DisconnectCallback());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Ignored {}, already closed", frame);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Ignored {}, already closed", frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -671,60 +661,42 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
@Override
|
@Override
|
||||||
public boolean close(int error, String reason, Callback callback)
|
public boolean close(int error, String reason, Callback callback)
|
||||||
{
|
{
|
||||||
while (true)
|
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.LOCALLY_CLOSED))
|
||||||
{
|
{
|
||||||
CloseState current = closed.get();
|
if (LOG.isDebugEnabled())
|
||||||
if (current == CloseState.NOT_CLOSED)
|
LOG.debug("Closing {}/{}", error, reason);
|
||||||
{
|
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, error, reason);
|
||||||
if (closed.compareAndSet(current, CloseState.LOCALLY_CLOSED))
|
control(null, callback, closeFrame);
|
||||||
{
|
return true;
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Closing {}/{}", error, reason);
|
|
||||||
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, error, reason);
|
|
||||||
control(null, callback, closeFrame);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Ignoring close {}/{}, already closed", error, reason);
|
|
||||||
callback.succeeded();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Ignoring close {}/{}, already closed", error, reason);
|
||||||
|
callback.succeeded();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> shutdown()
|
public CompletableFuture<Void> shutdown()
|
||||||
{
|
{
|
||||||
while (true)
|
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.LOCALLY_CLOSED))
|
||||||
{
|
{
|
||||||
CloseState current = closed.get();
|
if (LOG.isDebugEnabled())
|
||||||
if (current == CloseState.NOT_CLOSED)
|
LOG.debug("Shutting down {}", this);
|
||||||
{
|
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, ErrorCode.NO_ERROR.code, "shutdown");
|
||||||
if (closed.compareAndSet(current, CloseState.LOCALLY_CLOSED))
|
shutdownCallback = new Callback.Completable();
|
||||||
{
|
// Only send the close frame when we can flip Hi and Lo = 0, see onStreamClosed().
|
||||||
if (LOG.isDebugEnabled())
|
if (streamCount.compareAndSet(0, 1, 0, 0))
|
||||||
LOG.debug("Shutting down {}", this);
|
control(null, shutdownCallback, closeFrame);
|
||||||
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, ErrorCode.NO_ERROR.code, "shutdown");
|
return shutdownCallback;
|
||||||
closeCallback = new Callback.Completable();
|
|
||||||
// Only send the close frame when we can flip Hi and Lo = 0, see onStreamClosed().
|
|
||||||
if (streamCount.compareAndSet(0, 1, 0, 0))
|
|
||||||
control(null, closeCallback, closeFrame);
|
|
||||||
return closeCallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Ignoring shutdown, already closed");
|
|
||||||
Callback.Completable result = closeCallback;
|
|
||||||
// Result may be null if the shutdown is in progress,
|
|
||||||
// don't wait and return a completed CompletableFuture.
|
|
||||||
return result != null ? result : CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Ignoring shutdown, already closed");
|
||||||
|
Callback.Completable result = shutdownCallback;
|
||||||
|
// Result may be null if the shutdown is in progress,
|
||||||
|
// don't wait and return a completed CompletableFuture.
|
||||||
|
return result != null ? result : CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GoAwayFrame newGoAwayFrame(CloseState closeState, int error, String reason)
|
private GoAwayFrame newGoAwayFrame(CloseState closeState, int error, String reason)
|
||||||
|
@ -1075,13 +1047,23 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
||||||
|
|
||||||
protected void onStreamClosed(IStream stream)
|
protected void onStreamClosed(IStream stream)
|
||||||
{
|
{
|
||||||
if (streamCount.addAndGetLo(-1) == 0)
|
Callback callback = null;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
Callback.Completable callback = closeCallback;
|
long encoded = streamCount.get();
|
||||||
// Only send the close frame if we can flip Hi, see shutdown().
|
int closed = AtomicBiInteger.getHi(encoded);
|
||||||
if (callback != null && streamCount.compareAndSet(0, 1, 0, 0))
|
int streams = AtomicBiInteger.getLo(encoded) - 1;
|
||||||
control(null, callback, closeFrame);
|
if (streams == 0 && closed == 0)
|
||||||
|
{
|
||||||
|
callback = shutdownCallback;
|
||||||
|
closed = 1;
|
||||||
|
}
|
||||||
|
if (streamCount.compareAndSet(encoded, closed, streams))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
// Only send the close frame if we can flip Hi and Lo = 0, see shutdown().
|
||||||
|
if (callback != null)
|
||||||
|
control(null, callback, closeFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue