Skip to content

Commit

Permalink
Add ngtcp2_early_data_rejected callback
Browse files Browse the repository at this point in the history
Add ngtcp2_early_data_rejected callback in order to handle the
situation that client decided not to attempt early data before sending
its first flight.
  • Loading branch information
tatsuhiro-t committed Oct 14, 2022
1 parent fa3f43f commit ec59b87
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 19 deletions.
5 changes: 4 additions & 1 deletion crypto/boringssl/boringssl.c
Expand Up @@ -423,7 +423,10 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,

SSL_reset_early_data_reject(ssl);

ngtcp2_conn_early_data_rejected(conn);
rv = ngtcp2_conn_early_data_rejected(conn);
if (rv != 0) {
return -1;
}

goto retry;
default:
Expand Down
6 changes: 5 additions & 1 deletion crypto/picotls/picotls.c
Expand Up @@ -379,7 +379,11 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
if (!ngtcp2_conn_is_server(conn) &&
cptls->handshake_properties.client.early_data_acceptance ==
PTLS_EARLY_DATA_REJECTED) {
ngtcp2_conn_early_data_rejected(conn);
rv = ngtcp2_conn_early_data_rejected(conn);
if (rv != 0) {
rv = -1;
goto fin;
}
}

for (i = 0; i < 4; ++i) {
Expand Down
38 changes: 30 additions & 8 deletions examples/client.cc
Expand Up @@ -302,13 +302,15 @@ int Client::handshake_completed() {
std::cerr << "Early data was rejected by server" << std::endl;
}

ngtcp2_conn_early_data_rejected(conn_);

nghttp3_conn_del(httpconn_);
httpconn_ = nullptr;

nstreams_done_ = 0;
streams_.clear();
// Some TLS backends only report early data rejection after
// handshake completion (e.g., OpenSSL). For TLS backends which
// report it early (e.g., BoringSSL and PicoTLS), the following
// functions are noop.
if (auto rv = ngtcp2_conn_early_data_rejected(conn_); rv != 0) {
std::cerr << "ngtcp2_conn_early_data_rejected: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}

if (setup_httpconn() != 0) {
return -1;
Expand Down Expand Up @@ -602,14 +604,33 @@ int recv_rx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level, void *user_data) {
}

auto c = static_cast<Client *>(user_data);
if (!c->get_early_data() && c->setup_httpconn() != 0) {
if ((!c->get_early_data() || ngtcp2_conn_get_early_data_rejected(conn)) &&
c->setup_httpconn() != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}

return 0;
}
} // namespace

namespace {
int early_data_rejected(ngtcp2_conn *conn, void *user_data) {
auto c = static_cast<Client *>(user_data);

c->early_data_rejected();

return 0;
}
} // namespace

void Client::early_data_rejected() {
nghttp3_conn_del(httpconn_);
httpconn_ = nullptr;

nstreams_done_ = 0;
streams_.clear();
}

int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
const char *addr, const char *port,
TLSClientContext &tls_ctx) {
Expand Down Expand Up @@ -667,6 +688,7 @@ int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
ngtcp2_crypto_version_negotiation_cb,
::recv_rx_key,
nullptr, // recv_tx_key
::early_data_rejected,
};

ngtcp2_cid scid, dcid;
Expand Down
1 change: 1 addition & 0 deletions examples/client.h
Expand Up @@ -137,6 +137,7 @@ class Client : public ClientBase {
const std::vector<uint32_t> &get_offered_versions() const;

bool get_early_data() const;
void early_data_rejected();

private:
std::vector<Endpoint> endpoints_;
Expand Down
1 change: 1 addition & 0 deletions examples/gtlssimpleclient.c
Expand Up @@ -362,6 +362,7 @@ static int client_quic_init(struct client *c,
ngtcp2_crypto_version_negotiation_cb,
NULL, /* recv_rx_key */
NULL, /* recv_tx_key */
NULL, /* early_data_rejected */
};
ngtcp2_cid dcid, scid;
ngtcp2_settings settings;
Expand Down
29 changes: 25 additions & 4 deletions examples/h09client.cc
Expand Up @@ -296,10 +296,15 @@ int Client::handshake_completed() {
std::cerr << "Early data was rejected by server" << std::endl;
}

ngtcp2_conn_early_data_rejected(conn_);

nstreams_done_ = 0;
streams_.clear();
// Some TLS backends only report early data rejection after
// handshake completion (e.g., OpenSSL). For TLS backends which
// report it early (e.g., BoringSSL and PicoTLS), the following
// functions are noop.
if (auto rv = ngtcp2_conn_early_data_rejected(conn_); rv != 0) {
std::cerr << "ngtcp2_conn_early_data_rejected: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
}

if (!config.quiet) {
Expand Down Expand Up @@ -560,6 +565,21 @@ int recv_new_token(ngtcp2_conn *conn, const ngtcp2_vec *token,
}
} // namespace

namespace {
int early_data_rejected(ngtcp2_conn *conn, void *user_data) {
auto c = static_cast<Client *>(user_data);

c->early_data_rejected();

return 0;
}
} // namespace

void Client::early_data_rejected() {
nstreams_done_ = 0;
streams_.clear();
}

int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
const char *addr, const char *port,
TLSClientContext &tls_ctx) {
Expand Down Expand Up @@ -617,6 +637,7 @@ int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
ngtcp2_crypto_version_negotiation_cb,
nullptr, // recv_rx_key
nullptr, // recv_tx_key
::early_data_rejected,
};

ngtcp2_cid scid, dcid;
Expand Down
2 changes: 2 additions & 0 deletions examples/h09client.h
Expand Up @@ -141,6 +141,8 @@ class Client : public ClientBase {

const std::vector<uint32_t> &get_offered_versions() const;

void early_data_rejected();

private:
std::vector<Endpoint> endpoints_;
Address remote_addr_;
Expand Down
1 change: 1 addition & 0 deletions examples/simpleclient.c
Expand Up @@ -323,6 +323,7 @@ static int client_quic_init(struct client *c,
ngtcp2_crypto_version_negotiation_cb,
NULL, /* recv_rx_key */
NULL, /* recv_tx_key */
NULL, /* early_data_rejected */
};
ngtcp2_cid dcid, scid;
ngtcp2_settings settings;
Expand Down
39 changes: 36 additions & 3 deletions lib/includes/ngtcp2/ngtcp2.h
Expand Up @@ -3319,6 +3319,18 @@ typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version,
typedef int (*ngtcp2_recv_key)(ngtcp2_conn *conn, ngtcp2_crypto_level level,
void *user_data);

/**
* @functypedef
*
* :type:`ngtcp2_early_data_rejected` is invoked when early data was
* rejected by server, or client decided not to attempt early data.
*
* The callback function must return 0 if it succeeds. Returning
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
* immediately.
*/
typedef int (*ngtcp2_early_data_rejected)(ngtcp2_conn *conn, void *user_data);

#define NGTCP2_CALLBACKS_VERSION_V1 1
#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_VERSION_V1

Expand Down Expand Up @@ -3577,6 +3589,13 @@ typedef struct ngtcp2_callbacks {
* :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`.
*/
ngtcp2_recv_key recv_tx_key;
/**
* :member:`ngtcp2_early_data_rejected` is a callback function which
* is invoked when an attempt to send early data by client was
* rejected by server, or client decided not to attempt early data.
* This callback function is only used by client.
*/
ngtcp2_early_data_rejected early_data_rejected;
} ngtcp2_callbacks;

/**
Expand Down Expand Up @@ -4816,8 +4835,8 @@ NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn);
* @function
*
* `ngtcp2_conn_early_data_rejected` tells |conn| that early data was
* rejected by a server. |conn| discards the following connection
* states:
* rejected by a server, or client decided not to attempt early data
* for some reason. |conn| discards the following connection states:
*
* - Any opended streams.
* - Stream identifier allocations.
Expand All @@ -4827,8 +4846,22 @@ NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn);
*
* Application which wishes to retransmit early data, it has to open
* streams and send stream data again.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
* User callback failed
*/
NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn);

/**
* @function
*
* `ngtcp2_conn_get_early_data_rejected` returns nonzero if
* `ngtcp2_conn_early_data_rejected` has been called.
*/
NGTCP2_EXTERN void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn);
NGTCP2_EXTERN int ngtcp2_conn_get_early_data_rejected(ngtcp2_conn *conn);

/**
* @function
Expand Down
14 changes: 12 additions & 2 deletions lib/ngtcp2_conn.c
Expand Up @@ -12586,14 +12586,24 @@ static void conn_discard_early_data_state(ngtcp2_conn *conn) {
}
}

void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) {
return;
return 0;
}

conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED;

conn_discard_early_data_state(conn);

if (conn->callbacks.early_data_rejected) {
return conn->callbacks.early_data_rejected(conn, conn->user_data);
}

return 0;
}

int ngtcp2_conn_get_early_data_rejected(ngtcp2_conn *conn) {
return (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) != 0;
}

int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
Expand Down

0 comments on commit ec59b87

Please sign in to comment.