Skip to content

Commit

Permalink
Merge pull request #439 from h2o/kazuho/syncsend-datagram
Browse files Browse the repository at this point in the history
synchronous API for DATAGRAM frame emission
  • Loading branch information
kazuho authored Apr 20, 2021
2 parents 3bba3b8 + 6e88f86 commit 839a934
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 43 deletions.
9 changes: 5 additions & 4 deletions include/quicly.h
Original file line number Diff line number Diff line change
Expand Up @@ -1061,11 +1061,12 @@ static int quicly_stream_has_receive_side(int is_client, quicly_stream_id_t stre
*/
static int quicly_stream_is_self_initiated(quicly_stream_t *stream);
/**
* Registers a datagram frame payload to be sent. When the applications calls `quicly_send` the first time after registering the
* datagram frame payload, the payload is either sent or the reference is discarded. Until then, it is the caller's responsibility
* to retain the memory pointed to by `payload`. At the moment, DATAFRAM frames are not congestion controlled.
* Sends QUIC DATAGRAM frames. Some of the frames being provided may get dropped.
* Notes:
* * At the moment, emission of QUIC packets carrying DATAGRAM frames is not congestion controlled.
* * While the API is designed to look like synchronous, application still has to call `quicly_send` for the time being.
*/
void quicly_set_datagram_frame(quicly_conn_t *conn, ptls_iovec_t payload);
void quicly_send_datagram_frames(quicly_conn_t *conn, ptls_iovec_t *datagrams, size_t num_datagrams);
/**
*
*/
Expand Down
58 changes: 41 additions & 17 deletions lib/quicly.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,12 @@ struct st_quicly_conn_t {
*/
quicly_retire_cid_set_t retire_cid;
/**
* DATAGRAM frame payload to be sent
* payload of DATAGRAM frames to be sent
*/
ptls_iovec_t datagram_frame_payload;
struct {
ptls_iovec_t payloads[10];
size_t count;
} datagram_frame_payloads;
} egress;
/**
* crypto data
Expand Down Expand Up @@ -552,6 +555,15 @@ static void dispose_cipher(struct st_quicly_cipher_context_t *ctx)
ptls_cipher_free(ctx->header_protection);
}

static void clear_datagram_frame_payloads(quicly_conn_t *conn)
{
for (size_t i = 0; i != conn->egress.datagram_frame_payloads.count; ++i) {
free(conn->egress.datagram_frame_payloads.payloads[i].base);
conn->egress.datagram_frame_payloads.payloads[i] = ptls_iovec_init(NULL, 0);
}
conn->egress.datagram_frame_payloads.count = 0;
}

static int is_retry(quicly_conn_t *conn)
{
return conn->retry_scid.len != UINT8_MAX;
Expand Down Expand Up @@ -1502,6 +1514,7 @@ void quicly_free(quicly_conn_t *conn)
update_open_count(conn->super.ctx, -1);

destroy_all_streams(conn, 0, 1);
clear_datagram_frame_payloads(conn);

quicly_maxsender_dispose(&conn->ingress.max_data.sender);
quicly_maxsender_dispose(&conn->ingress.max_streams.uni);
Expand Down Expand Up @@ -2767,14 +2780,12 @@ static int on_ack_retire_connection_id(quicly_sentmap_t *map, const quicly_sent_

static int should_send_datagram_frame(quicly_conn_t *conn)
{
if (conn->egress.datagram_frame_payload.base == NULL)
if (conn->egress.datagram_frame_payloads.count == 0)
return 0;
if (conn->application == NULL)
return 0;
if (conn->application->cipher.egress.key.aead == NULL)
return 0;
if (conn->super.remote.transport_params.max_datagram_frame_size < conn->egress.datagram_frame_payload.len)
return 0;
return 1;
}

Expand Down Expand Up @@ -4204,15 +4215,19 @@ static int do_send(quicly_conn_t *conn, quicly_send_context_t *s)
* This is because we do not have a way to retract the generation of a QUIC packet.
* * Does not notify the application that the frame was dropped internally. */
if (should_send_datagram_frame(conn)) {
size_t required_space = quicly_datagram_frame_capacity(conn->egress.datagram_frame_payload);
if ((ret = _do_allocate_frame(conn, s, required_space, 1)) != 0)
goto Exit;
if (s->dst_end - s->dst >= required_space) {
s->dst = quicly_encode_datagram_frame(s->dst, conn->egress.datagram_frame_payload);
QUICLY_PROBE(DATAGRAM_SEND, conn, conn->stash.now, conn->egress.datagram_frame_payload.base,
conn->egress.datagram_frame_payload.len);
conn->egress.datagram_frame_payload = ptls_iovec_init(NULL, 0);
++conn->super.stats.num_frames_sent.datagram;
for (size_t i = 0; i != conn->egress.datagram_frame_payloads.count; ++i) {
ptls_iovec_t *payload = conn->egress.datagram_frame_payloads.payloads + i;
size_t required_space = quicly_datagram_frame_capacity(*payload);
if ((ret = _do_allocate_frame(conn, s, required_space, 1)) != 0)
goto Exit;
if (s->dst_end - s->dst >= required_space) {
s->dst = quicly_encode_datagram_frame(s->dst, *payload);
QUICLY_PROBE(DATAGRAM_SEND, conn, conn->stash.now, payload->base, payload->len);
} else {
/* FIXME: At the moment, we add a padding because we do not have a way to reclaim allocated space, and because
* it is forbidden to send an empty QUIC packet. */
*s->dst++ = QUICLY_FRAME_TYPE_PADDING;
}
}
}
if (!ack_only) {
Expand Down Expand Up @@ -4343,9 +4358,18 @@ static int do_send(quicly_conn_t *conn, quicly_send_context_t *s)
return ret;
}

void quicly_set_datagram_frame(quicly_conn_t *conn, ptls_iovec_t payload)
void quicly_send_datagram_frames(quicly_conn_t *conn, ptls_iovec_t *datagrams, size_t num_datagrams)
{
conn->egress.datagram_frame_payload = payload;
for (size_t i = 0; i != num_datagrams; ++i) {
if (conn->egress.datagram_frame_payloads.count == PTLS_ELEMENTSOF(conn->egress.datagram_frame_payloads.payloads))
break;
void *copied;
if ((copied = malloc(datagrams[i].len)) == NULL)
break;
memcpy(copied, datagrams[i].base, datagrams[i].len);
conn->egress.datagram_frame_payloads.payloads[conn->egress.datagram_frame_payloads.count++] =
ptls_iovec_init(copied, datagrams[i].len);
}
}

int quicly_send(quicly_conn_t *conn, quicly_address_t *dest, quicly_address_t *src, struct iovec *datagrams, size_t *num_datagrams,
Expand Down Expand Up @@ -4403,7 +4427,7 @@ int quicly_send(quicly_conn_t *conn, quicly_address_t *dest, quicly_address_t *s
assert_consistency(conn, 1);

Exit:
conn->egress.datagram_frame_payload = ptls_iovec_init(NULL, 0);
clear_datagram_frame_payloads(conn);
if (s.num_datagrams != 0) {
*dest = conn->super.remote.address;
*src = conn->super.local.address;
Expand Down
25 changes: 3 additions & 22 deletions src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ FILE *quicly_trace_fp = NULL;
static unsigned verbosity = 0;
static int suppress_output = 0, send_datagram_frame = 0;
static int64_t enqueue_requests_at = 0, request_interval = 0;
static void *datagram_frame_payload_buf;

static void hexdump(const char *title, const uint8_t *p, size_t l)
{
Expand Down Expand Up @@ -489,34 +488,15 @@ static int send_pending(int fd, quicly_conn_t *conn)
if ((ret = quicly_send(conn, &dest, &src, packets, &num_packets, buf, sizeof(buf))) == 0 && num_packets != 0)
send_packets(fd, &dest.sa, packets, num_packets);

if (datagram_frame_payload_buf != NULL) {
free(datagram_frame_payload_buf);
datagram_frame_payload_buf = NULL;
}

return ret;
}

static void set_datagram_frame(quicly_conn_t *conn, ptls_iovec_t payload)
{
if (datagram_frame_payload_buf != NULL)
free(datagram_frame_payload_buf);

/* replace payload.base with an allocated buffer */
datagram_frame_payload_buf = malloc(payload.len);
memcpy(datagram_frame_payload_buf, payload.base, payload.len);
payload.base = datagram_frame_payload_buf;

/* set data to be sent. The buffer is being freed in `send_pending` after `quicly_send` is being called. */
quicly_set_datagram_frame(conn, payload);
}

static void on_receive_datagram_frame(quicly_receive_datagram_frame_t *self, quicly_conn_t *conn, ptls_iovec_t payload)
{
printf("DATAGRAM: %.*s\n", (int)payload.len, payload.base);
/* send responds with a datagram frame */
if (!quicly_is_client(conn))
set_datagram_frame(conn, payload);
quicly_send_datagram_frames(conn, &payload, 1);
}

static void enqueue_requests(quicly_conn_t *conn)
Expand Down Expand Up @@ -618,7 +598,8 @@ static int run_client(int fd, struct sockaddr *sa, const char *host)
quicly_receive(conn, NULL, &sa, &packet);
if (send_datagram_frame && quicly_connection_is_ready(conn)) {
const char *message = "hello datagram!";
set_datagram_frame(conn, ptls_iovec_init(message, strlen(message)));
ptls_iovec_t datagram = ptls_iovec_init(message, strlen(message));
quicly_send_datagram_frames(conn, &datagram, 1);
send_datagram_frame = 0;
}
}
Expand Down

0 comments on commit 839a934

Please sign in to comment.