Issue #6728 - QUIC and HTTP/3

- QPACK exceptions now use long instead of int, to be consistent with other error codes.
- Fixed ManagedSelector to count down the stop latches in finally blocks, so that they are always counted down even in case of exceptions.
- Improved exception handling in case of closes.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2021-10-22 13:26:12 +02:00
parent 8e81c80e1e
commit c879358777
9 changed files with 63 additions and 44 deletions

View File

@ -86,7 +86,7 @@ public abstract class BodyParser
}
}
protected void notifyStreamFailure(long streamId, int error, Throwable failure)
protected void notifyStreamFailure(long streamId, long error, Throwable failure)
{
try
{

View File

@ -13,23 +13,21 @@
package org.eclipse.jetty.http3.qpack;
@SuppressWarnings("serial")
public abstract class QpackException extends Exception
{
public static final int QPACK_DECOMPRESSION_FAILED = 0x200;
public static final int QPACK_ENCODER_STREAM_ERROR = 0x201;
public static final int QPACK_DECODER_STREAM_ERROR = 0x202;
public static final int H3_GENERAL_PROTOCOL_ERROR = 0x0101;
public static final long QPACK_DECOMPRESSION_FAILED = 0x200;
public static final long QPACK_ENCODER_STREAM_ERROR = 0x201;
public static final long QPACK_DECODER_STREAM_ERROR = 0x202;
public static final long H3_GENERAL_PROTOCOL_ERROR = 0x0101;
private final long _errorCode;
private final int _errorCode;
QpackException(int errorCode, String messageFormat, Throwable cause)
QpackException(long errorCode, String messageFormat, Throwable cause)
{
super(messageFormat, cause);
_errorCode = errorCode;
}
public int getErrorCode()
public long getErrorCode()
{
return _errorCode;
}
@ -43,12 +41,12 @@ public abstract class QpackException extends Exception
*/
public static class StreamException extends QpackException
{
public StreamException(int errorCode, String messageFormat)
public StreamException(long errorCode, String messageFormat)
{
this(errorCode, messageFormat, null);
}
public StreamException(int errorCode, String messageFormat, Throwable cause)
public StreamException(long errorCode, String messageFormat, Throwable cause)
{
super(errorCode, messageFormat, cause);
}
@ -61,12 +59,12 @@ public abstract class QpackException extends Exception
*/
public static class SessionException extends QpackException
{
public SessionException(int errorCode, String message)
public SessionException(long errorCode, String message)
{
this(errorCode, message, null);
}
public SessionException(int errorCode, String message, Throwable cause)
public SessionException(long errorCode, String message, Throwable cause)
{
super(errorCode, message, cause);
}

View File

@ -36,6 +36,11 @@
<artifactId>jetty-alpn-java-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>

View File

@ -13,8 +13,10 @@
package org.eclipse.jetty.http3.tests;
import java.lang.management.ManagementFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import javax.management.MBeanServer;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
@ -29,6 +31,7 @@ import org.eclipse.jetty.http3.client.http.ClientConnectionFactoryOverHTTP3;
import org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory;
import org.eclipse.jetty.http3.server.HTTP3ServerConnector;
import org.eclipse.jetty.http3.server.RawHTTP3ServerConnectionFactory;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
@ -79,6 +82,8 @@ public class AbstractClientServerTest
server = new Server(serverThreads);
connector = new HTTP3ServerConnector(server, sslContextFactory, serverConnectionFactory);
server.addConnector(connector);
MBeanContainer mbeanContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
server.addBean(mbeanContainer);
}
protected void startClient() throws Exception
@ -88,6 +93,9 @@ public class AbstractClientServerTest
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
httpClient.setExecutor(clientThreads);
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
httpClient.addBean(mbeanContainer);
httpClient.start();
}

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.io;
import java.io.Closeable;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedSelectorException;

View File

@ -171,7 +171,18 @@ public abstract class QuicConnection extends AbstractConnection
if (LOG.isDebugEnabled())
LOG.debug("closing connection {}", this);
// Propagate the close inward to the protocol-specific session.
sessions.values().forEach(session -> session.inwardClose(QuicErrorCode.NO_ERROR.code(), "stop"));
for (QuicSession session : sessions.values())
{
try
{
session.inwardClose(QuicErrorCode.NO_ERROR.code(), "stop");
}
catch (Throwable x)
{
if (LOG.isTraceEnabled())
LOG.trace("could not close {}", session, x);
}
}
}
}

View File

@ -219,12 +219,6 @@ public abstract class QuicSession extends ContainerLifeCycle
return flushed;
}
public void flushFinished(long streamId) throws IOException
{
quicheConnection.feedFinForStream(streamId);
flush();
}
public boolean isFinished(long streamId)
{
return quicheConnection.isStreamFinished(streamId);
@ -397,7 +391,6 @@ public abstract class QuicSession extends ContainerLifeCycle
public void inwardClose(long error, String reason)
{
protocolSession.inwardClose(error, reason);
flush();
}
public void outwardClose(long error, String reason)

View File

@ -70,12 +70,14 @@ public interface LibQuiche extends Library
// The minimum length of Initial packets sent by a client.
int QUICHE_MIN_CLIENT_INITIAL_LEN = 1200;
interface quiche_cc_algorithm {
interface quiche_cc_algorithm
{
int QUICHE_CC_RENO = 0,
QUICHE_CC_CUBIC = 1;
QUICHE_CC_CUBIC = 1;
}
interface quiche_error {
interface quiche_error
{
// There is no more work to do.
long QUICHE_ERR_DONE = -1,
@ -236,13 +238,15 @@ public interface LibQuiche extends Library
public byte dummy;
}
@Structure.FieldOrder({"recv", "sent", "lost", "retrans", "rtt", "cwnd", "sent_bytes", "recv_bytes", "lost_bytes",
"stream_retrans_bytes", "pmtu", "delivery_rate", "peer_max_idle_timeout",
"peer_max_udp_payload_size", "peer_initial_max_data", "peer_initial_max_stream_data_bidi_local",
"peer_initial_max_stream_data_bidi_remote", "peer_initial_max_stream_data_uni",
"peer_initial_max_streams_bidi", "peer_initial_max_streams_uni", "peer_ack_delay_exponent",
"peer_max_ack_delay", "peer_disable_active_migration", "peer_active_conn_id_limit",
"peer_max_datagram_frame_size"})
@Structure.FieldOrder({
"recv", "sent", "lost", "retrans", "rtt", "cwnd", "sent_bytes", "recv_bytes", "lost_bytes",
"stream_retrans_bytes", "pmtu", "delivery_rate", "peer_max_idle_timeout",
"peer_max_udp_payload_size", "peer_initial_max_data", "peer_initial_max_stream_data_bidi_local",
"peer_initial_max_stream_data_bidi_remote", "peer_initial_max_stream_data_uni",
"peer_initial_max_streams_bidi", "peer_initial_max_streams_uni", "peer_ack_delay_exponent",
"peer_max_ack_delay", "peer_disable_active_migration", "peer_active_conn_id_limit",
"peer_max_datagram_frame_size"
})
class quiche_stats extends Structure
{
// The number of QUIC packets received on this connection.
@ -335,11 +339,11 @@ public interface LibQuiche extends Library
interface packet_type
{
byte INITIAL = 1,
RETRY = 2,
HANDSHAKE = 3,
ZERO_RTT = 4,
SHORT = 5,
VERSION_NEGOTIATION = 6;
RETRY = 2,
HANDSHAKE = 3,
ZERO_RTT = 4,
SHORT = 5,
VERSION_NEGOTIATION = 6;
static String typeToString(byte type)
{
@ -377,10 +381,10 @@ public interface LibQuiche extends Library
// Writes a retry packet.
ssize_t quiche_retry(byte[] scid, size_t scid_len,
byte[] dcid, size_t dcid_len,
byte[] new_scid, size_t new_scid_len,
byte[] token, size_t token_len,
uint32_t version, ByteBuffer out, size_t out_len);
byte[] dcid, size_t dcid_len,
byte[] new_scid, size_t new_scid_len,
byte[] token, size_t token_len,
uint32_t version, ByteBuffer out, size_t out_len);
// Creates a new server-side connection.
quiche_conn quiche_accept(byte[] scid, size_t scid_len, byte[] odcid, size_t odcid_len, sockaddr from, size_t from_len, quiche_config config);
@ -460,7 +464,8 @@ public interface LibQuiche extends Library
public byte dummy;
}
interface quiche_shutdown {
interface quiche_shutdown
{
int QUICHE_SHUTDOWN_READ = 0,
QUICHE_SHUTDOWN_WRITE = 1;
}

View File

@ -194,13 +194,13 @@ public class QuicServerConnector extends AbstractNetworkConnector
try
{
datagramChannel.bind(bindAddress);
return datagramChannel;
}
catch (Throwable e)
{
IO.close(datagramChannel);
throw new IOException("Failed to bind to " + bindAddress, e);
}
return datagramChannel;
}
@Override