handle quiche connections timeouts
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
parent
f0725d09d0
commit
25ee5e35ac
|
@ -80,6 +80,11 @@ public class QuicConnection extends AbstractConnection
|
||||||
quicheConfig.setApplicationProtos(getProtocols().toArray(new String[0]));
|
quicheConfig.setApplicationProtos(getProtocols().toArray(new String[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onClose(QuicheConnectionId quicheConnectionId)
|
||||||
|
{
|
||||||
|
sessions.remove(quicheConnectionId);
|
||||||
|
}
|
||||||
|
|
||||||
private Collection<String> getProtocols()
|
private Collection<String> getProtocols()
|
||||||
{
|
{
|
||||||
// TODO get the protocols from the connector
|
// TODO get the protocols from the connector
|
||||||
|
@ -141,7 +146,7 @@ public class QuicConnection extends AbstractConnection
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
session = new QuicSession(connector, quicheConnection, this, remoteAddress);
|
session = new QuicSession(connector, quicheConnectionId, quicheConnection, this, remoteAddress);
|
||||||
sessions.putIfAbsent(quicheConnectionId, session);
|
sessions.putIfAbsent(quicheConnectionId, session);
|
||||||
session.flush();
|
session.flush();
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
|
|
|
@ -19,11 +19,15 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.eclipse.jetty.http3.quiche.QuicheConnection;
|
import org.eclipse.jetty.http3.quiche.QuicheConnection;
|
||||||
|
import org.eclipse.jetty.http3.quiche.QuicheConnectionId;
|
||||||
import org.eclipse.jetty.http3.quiche.ffi.LibQuiche;
|
import org.eclipse.jetty.http3.quiche.ffi.LibQuiche;
|
||||||
|
import org.eclipse.jetty.io.AbstractEndPoint;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.CyclicTimeout;
|
||||||
import org.eclipse.jetty.io.RuntimeIOException;
|
import org.eclipse.jetty.io.RuntimeIOException;
|
||||||
import org.eclipse.jetty.server.ConnectionFactory;
|
import org.eclipse.jetty.server.ConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
@ -37,19 +41,22 @@ public class QuicSession
|
||||||
{
|
{
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(QuicSession.class);
|
private static final Logger LOG = LoggerFactory.getLogger(QuicSession.class);
|
||||||
|
|
||||||
private final Flusher flusher = new Flusher();
|
private final Flusher flusher;
|
||||||
private final Connector connector;
|
private final Connector connector;
|
||||||
|
private final QuicheConnectionId quicheConnectionId;
|
||||||
private final QuicheConnection quicheConnection;
|
private final QuicheConnection quicheConnection;
|
||||||
private final QuicConnection connection;
|
private final QuicConnection connection;
|
||||||
private final ConcurrentMap<Long, QuicStreamEndPoint> endpoints = new ConcurrentHashMap<>();
|
private final ConcurrentMap<Long, QuicStreamEndPoint> endpoints = new ConcurrentHashMap<>();
|
||||||
private InetSocketAddress remoteAddress;
|
private InetSocketAddress remoteAddress;
|
||||||
|
|
||||||
QuicSession(Connector connector, QuicheConnection quicheConnection, QuicConnection connection, InetSocketAddress remoteAddress)
|
QuicSession(Connector connector, QuicheConnectionId quicheConnectionId, QuicheConnection quicheConnection, QuicConnection connection, InetSocketAddress remoteAddress)
|
||||||
{
|
{
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
|
this.quicheConnectionId = quicheConnectionId;
|
||||||
this.quicheConnection = quicheConnection;
|
this.quicheConnection = quicheConnection;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.remoteAddress = remoteAddress;
|
this.remoteAddress = remoteAddress;
|
||||||
|
this.flusher = new Flusher(connector.getScheduler());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int fill(long streamId, ByteBuffer buffer) throws IOException
|
public int fill(long streamId, ByteBuffer buffer) throws IOException
|
||||||
|
@ -182,21 +189,76 @@ public class QuicSession
|
||||||
return endPoint;
|
return endPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void close()
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Closing QUIC session {}", this);
|
||||||
|
endpoints.values().forEach(AbstractEndPoint::close);
|
||||||
|
endpoints.clear();
|
||||||
|
flusher.close();
|
||||||
|
quicheConnection.dispose();
|
||||||
|
connection.onClose(quicheConnectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return getClass().getSimpleName() + " id=" + quicheConnectionId;
|
||||||
|
}
|
||||||
|
|
||||||
private class Flusher extends IteratingCallback
|
private class Flusher extends IteratingCallback
|
||||||
{
|
{
|
||||||
|
private final CyclicTimeout timeout;
|
||||||
private ByteBuffer addressBuffer;
|
private ByteBuffer addressBuffer;
|
||||||
private ByteBuffer cipherBuffer;
|
private ByteBuffer cipherBuffer;
|
||||||
|
|
||||||
|
public Flusher(Scheduler scheduler)
|
||||||
|
{
|
||||||
|
timeout = new CyclicTimeout(scheduler) {
|
||||||
@Override
|
@Override
|
||||||
protected Action process() throws Throwable
|
public void onTimeoutExpired()
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("quiche timeout callback");
|
||||||
|
quicheConnection.onTimeout();
|
||||||
|
if (quicheConnection.isConnectionClosed())
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("quiche connection closed after timeout, re-flushing");
|
||||||
|
iterate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
super.close();
|
||||||
|
timeout.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Action process() throws IOException
|
||||||
{
|
{
|
||||||
ByteBufferPool byteBufferPool = connector.getByteBufferPool();
|
ByteBufferPool byteBufferPool = connector.getByteBufferPool();
|
||||||
addressBuffer = QuicConnection.encodeInetSocketAddress(byteBufferPool, remoteAddress);
|
addressBuffer = QuicConnection.encodeInetSocketAddress(byteBufferPool, remoteAddress);
|
||||||
cipherBuffer = byteBufferPool.acquire(LibQuiche.QUICHE_MIN_CLIENT_INITIAL_LEN + ServerDatagramEndPoint.ENCODED_ADDRESS_LENGTH, true);
|
cipherBuffer = byteBufferPool.acquire(LibQuiche.QUICHE_MIN_CLIENT_INITIAL_LEN + ServerDatagramEndPoint.ENCODED_ADDRESS_LENGTH, true);
|
||||||
int pos = BufferUtil.flipToFill(cipherBuffer);
|
int pos = BufferUtil.flipToFill(cipherBuffer);
|
||||||
int drained = quicheConnection.drainCipherText(cipherBuffer);
|
int drained = quicheConnection.drainCipherText(cipherBuffer);
|
||||||
|
long nextTimeoutInMs = quicheConnection.nextTimeout();
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("next quiche timeout: {} ms", nextTimeoutInMs);
|
||||||
|
if (nextTimeoutInMs > -1)
|
||||||
|
timeout.schedule(nextTimeoutInMs, TimeUnit.MILLISECONDS); // TODO is this re-scheduling cancelling the previous timeout?
|
||||||
|
else
|
||||||
|
timeout.cancel();
|
||||||
if (drained == 0)
|
if (drained == 0)
|
||||||
|
{
|
||||||
|
if (quicheConnection.isConnectionClosed())
|
||||||
|
QuicSession.this.close();
|
||||||
return Action.IDLE;
|
return Action.IDLE;
|
||||||
|
}
|
||||||
BufferUtil.flipToFlush(cipherBuffer, pos);
|
BufferUtil.flipToFlush(cipherBuffer, pos);
|
||||||
connection.write(this, addressBuffer, cipherBuffer);
|
connection.write(this, addressBuffer, cipherBuffer);
|
||||||
return Action.SCHEDULED;
|
return Action.SCHEDULED;
|
||||||
|
@ -217,7 +279,6 @@ public class QuicSession
|
||||||
ByteBufferPool byteBufferPool = connector.getByteBufferPool();
|
ByteBufferPool byteBufferPool = connector.getByteBufferPool();
|
||||||
byteBufferPool.release(addressBuffer);
|
byteBufferPool.release(addressBuffer);
|
||||||
byteBufferPool.release(cipherBuffer);
|
byteBufferPool.release(cipherBuffer);
|
||||||
connection.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue