Skip to content

Commit

Permalink
Propagate events for path related activities (#623)
Browse files Browse the repository at this point in the history
Motivation:

There are multiple events that can happen that are related to network
path (i.e. connection migration). We should allow the user to be
notified for all of them

Modifications:

- Introduce QuicPathEvent and related subclasses which will be fired
through the ChannelPipeline when a path related event happens

Result:

Be able to observe all events
  • Loading branch information
normanmaurer authored Nov 27, 2023
1 parent a6af06e commit b669d96
Show file tree
Hide file tree
Showing 6 changed files with 638 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
/*
* Copyright 2023 The Netty Project
*
* The Netty Project 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:
*
* https://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.
*/
package io.netty.incubator.codec.quic;

import java.net.InetSocketAddress;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
* A network path specific {@link QuicEvent}.
*/
public abstract class QuicPathEvent implements QuicEvent {

private final InetSocketAddress local;
private final InetSocketAddress remote;

QuicPathEvent(InetSocketAddress local, InetSocketAddress remote) {
this.local = requireNonNull(local, "local");
this.remote = requireNonNull(remote, "remote");
}

/**
* The local address of the network path.
*
* @return local
*/
public InetSocketAddress local() {
return local;
}

/**
* The remote address of the network path.
*
* @return local
*/
public InetSocketAddress remote() {
return remote;
}

@Override
public String toString() {
return "QuicPathEvent{" +
"local=" + local +
", remote=" + remote +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

QuicPathEvent that = (QuicPathEvent) o;
if (!Objects.equals(local, that.local)) {
return false;
}
return Objects.equals(remote, that.remote);
}

@Override
public int hashCode() {
int result = local != null ? local.hashCode() : 0;
result = 31 * result + (remote != null ? remote.hashCode() : 0);
return result;
}

public static final class New extends QuicPathEvent {
/**
* A new network path (local address, remote address) has been seen on a received packet.
* Note that this event is only triggered for servers, as the client is responsible from initiating new paths.
* The application may then probe this new path, if desired.
*
* @param local local address.
* @param remote remote address.
*/
public New(InetSocketAddress local, InetSocketAddress remote) {
super(local, remote);
}

@Override
public String toString() {
return "QuicPathEvent.New{" +
"local=" + local() +
", remote=" + remote() +
'}';
}
}

public static final class Validated extends QuicPathEvent {
/**
* The related network path between local and remote has been validated.
*
* @param local local address.
* @param remote remote address.
*/
public Validated(InetSocketAddress local, InetSocketAddress remote) {
super(local, remote);
}

@Override
public String toString() {
return "QuicPathEvent.Validated{" +
"local=" + local() +
", remote=" + remote() +
'}';
}
}

public static final class FailedValidation extends QuicPathEvent {
/**
* The related network path between local and remote failed to be validated.
* This network path will not be used anymore, unless the application requests probing this path again.
*
* @param local local address.
* @param remote remote address.
*/
public FailedValidation(InetSocketAddress local, InetSocketAddress remote) {
super(local, remote);
}

@Override
public String toString() {
return "QuicPathEvent.FailedValidation{" +
"local=" + local() +
", remote=" + remote() +
'}';
}
}

public static final class Closed extends QuicPathEvent {

/**
* The related network path between local and remote has been closed and is now unusable on this connection.
*
* @param local local address.
* @param remote remote address.
*/
public Closed(InetSocketAddress local, InetSocketAddress remote) {
super(local, remote);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
return super.equals(o);
}

@Override
public String toString() {
return "QuicPathEvent.Closed{" +
"local=" + local() +
", remote=" + remote() +
'}';
}
}

public static final class ReusedSourceConnectionId extends QuicPathEvent {
private final long seq;
private final InetSocketAddress oldLocal;
private final InetSocketAddress oldRemote;

/**
* The stack observes that the Source Connection ID with the given sequence number,
* initially used by the peer over the first pair of addresses, is now reused over
* the second pair of addresses.
*
* @param seq sequence number
* @param oldLocal old local address.
* @param oldRemote old remote address.
* @param local local address.
* @param remote remote address.
*/
public ReusedSourceConnectionId(long seq, InetSocketAddress oldLocal, InetSocketAddress oldRemote,
InetSocketAddress local, InetSocketAddress remote) {
super(local, remote);
this.seq = seq;
this.oldLocal = requireNonNull(oldLocal, "oldLocal");
this.oldRemote = requireNonNull(oldRemote, "oldRemote");
}

/**
* Source connection id sequence number.
*
* @return sequence number
*/
public long seq() {
return seq;
}

/**
* The old local address of the network path.
*
* @return local
*/
public InetSocketAddress oldLocal() {
return oldLocal;
}

/**
* The old remote address of the network path.
*
* @return local
*/
public InetSocketAddress oldRemote() {
return oldRemote;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}

ReusedSourceConnectionId that = (ReusedSourceConnectionId) o;

if (seq != that.seq) {
return false;
}
if (!Objects.equals(oldLocal, that.oldLocal)) {
return false;
}
return Objects.equals(oldRemote, that.oldRemote);
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (int) (seq ^ (seq >>> 32));
result = 31 * result + (oldLocal != null ? oldLocal.hashCode() : 0);
result = 31 * result + (oldRemote != null ? oldRemote.hashCode() : 0);
return result;
}

@Override
public String toString() {
return "QuicPathEvent.ReusedSourceConnectionId{" +
"seq=" + seq +
", oldLocal=" + oldLocal +
", oldRemote=" + oldRemote +
", local=" + local() +
", remote=" + remote() +
'}';
}
}

public static final class PeerMigrated extends QuicPathEvent {

/**
* The connection observed that the remote migrated over the network path denoted by the pair of addresses,
* i.e., non-probing packets have been received on this network path. This is a server side only event.
* Note that this event is only raised if the path has been validated.
*
* @param local local address.
* @param remote remote address.
*/
public PeerMigrated(InetSocketAddress local, InetSocketAddress remote) {
super(local, remote);
}

@Override
public String toString() {
return "QuicPathEvent.PeerMigrated{" +
"local=" + local() +
", remote=" + remote() +
'}';
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,14 @@ private static void loadNativeLibrary() {
*/
static final int QUICHE_CC_BBR = QuicheNativeStaticallyReferencedJniMethods.quiche_cc_bbr();


static final int QUICHE_PATH_EVENT_NEW = QuicheNativeStaticallyReferencedJniMethods.quiche_path_event_new();
static final int QUICHE_PATH_EVENT_VALIDATED = QuicheNativeStaticallyReferencedJniMethods.quiche_path_event_validated();
static final int QUICHE_PATH_EVENT_FAILED_VALIDATION = QuicheNativeStaticallyReferencedJniMethods.quiche_path_event_failed_validation();
static final int QUICHE_PATH_EVENT_CLOSED = QuicheNativeStaticallyReferencedJniMethods.quiche_path_event_closed();
static final int QUICHE_PATH_EVENT_REUSED_SOURCE_CONNECTION_ID = QuicheNativeStaticallyReferencedJniMethods.quiche_path_event_reused_source_connection_id();
static final int QUICHE_PATH_EVENT_PEER_MIGRATED = QuicheNativeStaticallyReferencedJniMethods.quiche_path_event_peer_migrated();

/**
* See <a href="https://github.com/cloudflare/quiche/blob/0.6.0/include/quiche.h#L105">quiche_version</a>.
*/
Expand Down Expand Up @@ -553,6 +561,16 @@ static native int quiche_conn_stream_priority(

static native byte[] quiche_conn_retired_scid_next(long connAddr);

static native long quiche_conn_path_event_next(long connAddr);
static native int quiche_path_event_type(long pathEvent);
static native void quiche_path_event_free(long pathEvent);
static native Object[] quiche_path_event_new(long pathEvent);
static native Object[] quiche_path_event_validated(long pathEvent);
static native Object[] quiche_path_event_failed_validation(long pathEvent);
static native Object[] quiche_path_event_closed(long pathEvent);
static native Object[] quiche_path_event_reused_source_connection_id(long pathEvent);
static native Object[] quiche_path_event_peer_migrated(long pathEvent);

/**
* See
* <a href="https://github.com/cloudflare/quiche/blob/0.6.0/include/quiche.h#L115">quiche_config_new</a>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,12 @@ final class QuicheNativeStaticallyReferencedJniMethods {
static native int sizeofTimeT();
static native int sizeofLong();

static native int quiche_path_event_new();
static native int quiche_path_event_validated();
static native int quiche_path_event_failed_validation();
static native int quiche_path_event_closed();
static native int quiche_path_event_reused_source_connection_id();
static native int quiche_path_event_peer_migrated();

private QuicheNativeStaticallyReferencedJniMethods() { }
}
Loading

0 comments on commit b669d96

Please sign in to comment.