diff options
author | Joel Galenson <jgalenson@google.com> | 2021-06-08 17:53:00 -0700 |
---|---|---|
committer | Joel Galenson <jgalenson@google.com> | 2021-06-21 11:43:07 -0700 |
commit | 96d408b0bb385e0d68122111acd9c9f3aefea6c1 (patch) | |
tree | f322aa3a0b30f78d0f83e74509d4af2e92f0f9e0 /examples | |
parent | ae2637dc45ccca8782c260803c0547a6b6d1641c (diff) | |
download | quiche-96d408b0bb385e0d68122111acd9c9f3aefea6c1.tar.gz |
Upgrade rust/crates/quiche to 0.9.0android-mainline-12.0.0_r99android-mainline-12.0.0_r96android-mainline-12.0.0_r95android-mainline-12.0.0_r94android-mainline-12.0.0_r93android-mainline-12.0.0_r84android-mainline-12.0.0_r83android-mainline-12.0.0_r82android-mainline-12.0.0_r81android-mainline-12.0.0_r80android-mainline-12.0.0_r8android-mainline-12.0.0_r79android-mainline-12.0.0_r77android-mainline-12.0.0_r70android-mainline-12.0.0_r67android-mainline-12.0.0_r66android-mainline-12.0.0_r65android-mainline-12.0.0_r64android-mainline-12.0.0_r63android-mainline-12.0.0_r6android-mainline-12.0.0_r59android-mainline-12.0.0_r58android-mainline-12.0.0_r57android-mainline-12.0.0_r53android-mainline-12.0.0_r52android-mainline-12.0.0_r51android-mainline-12.0.0_r49android-mainline-12.0.0_r40android-mainline-12.0.0_r38android-mainline-12.0.0_r37android-mainline-12.0.0_r35android-mainline-12.0.0_r34android-mainline-12.0.0_r32android-mainline-12.0.0_r25android-mainline-12.0.0_r23android-mainline-12.0.0_r20android-mainline-12.0.0_r18android-mainline-12.0.0_r17android-mainline-12.0.0_r16android-mainline-12.0.0_r15android-mainline-12.0.0_r14android-mainline-12.0.0_r126android-mainline-12.0.0_r125android-mainline-12.0.0_r124android-mainline-12.0.0_r123android-mainline-12.0.0_r122android-mainline-12.0.0_r114android-mainline-12.0.0_r110android-mainline-12.0.0_r109android-mainline-12.0.0_r108android-mainline-12.0.0_r107android-mainline-12.0.0_r100aml_wif_311811030aml_tet_311811050aml_sdk_311710000aml_pco_311011000android12-mainline-wifi-releaseandroid12-mainline-tethering-releaseandroid12-mainline-statsd-releaseandroid12-mainline-sdkext-releaseandroid12-mainline-resolv-releaseandroid12-mainline-permission-releaseandroid12-mainline-networkstack-releaseandroid12-mainline-conscrypt-releaseandroid12-mainline-captiveportallogin-release
Test: make
Change-Id: I438d6a167e6e0bbfe38785ba13c33a285d1c510b
Diffstat (limited to 'examples')
-rw-r--r-- | examples/Makefile | 4 | ||||
-rw-r--r-- | examples/client.c | 41 | ||||
-rw-r--r-- | examples/client.rs | 27 | ||||
-rw-r--r-- | examples/http3-client.c | 57 | ||||
-rw-r--r-- | examples/http3-client.rs | 38 | ||||
-rw-r--r-- | examples/http3-server.c | 36 | ||||
-rw-r--r-- | examples/http3-server.rs | 106 | ||||
-rw-r--r-- | examples/qpack-decode.rs | 4 | ||||
-rw-r--r-- | examples/qpack-encode.rs | 2 | ||||
-rw-r--r-- | examples/server.c | 53 | ||||
-rw-r--r-- | examples/server.rs | 82 |
11 files changed, 271 insertions, 179 deletions
diff --git a/examples/Makefile b/examples/Makefile index bb75e7d..5660bbd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -34,7 +34,7 @@ http3-server: http3-server.c $(INCLUDE_DIR)/quiche.h $(LIB_DIR)/libquiche.a $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(INCS) $(LIBS) $(LIB_DIR)/libquiche.a: $(shell find $(SOURCE_DIR) -type f -name '*.rs') - cd .. && cargo build --target-dir $(BUILD_DIR) + cd .. && cargo build --target-dir $(BUILD_DIR) --features ffi clean: - @$(RM) -rf client server http3-client http3-server build/ + @$(RM) -rf client server http3-client http3-server build/ *.dSYM/ diff --git a/examples/client.c b/examples/client.c index bca9781..0df9665 100644 --- a/examples/client.c +++ b/examples/client.c @@ -61,8 +61,11 @@ static void debug_log(const char *line, void *argp) { static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { static uint8_t out[MAX_DATAGRAM_SIZE]; + quiche_send_info send_info; + while (1) { - ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out)); + ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out), + &send_info); if (written == QUICHE_ERR_DONE) { fprintf(stderr, "done writing\n"); @@ -74,7 +77,10 @@ static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { return; } - ssize_t sent = send(conn_io->sock, out, written, 0); + ssize_t sent = sendto(conn_io->sock, out, written, 0, + (struct sockaddr *) &send_info.to, + send_info.to_len); + if (sent != written) { perror("failed to send"); return; @@ -96,7 +102,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { static uint8_t buf[65535]; while (1) { - ssize_t read = recv(conn_io->sock, buf, sizeof(buf), 0); + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len = sizeof(peer_addr); + memset(&peer_addr, 0, peer_addr_len); + + ssize_t read = recvfrom(conn_io->sock, buf, sizeof(buf), 0, + (struct sockaddr *) &peer_addr, + &peer_addr_len); if (read < 0) { if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { @@ -108,7 +120,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { return; } - ssize_t done = quiche_conn_recv(conn_io->conn, buf, read); + quiche_recv_info recv_info = { + (struct sockaddr *) &peer_addr, + + peer_addr_len, + }; + + ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info); if (done < 0) { fprintf(stderr, "failed to process packet\n"); @@ -228,11 +246,6 @@ int main(int argc, char *argv[]) { return -1; } - if (connect(sock, peer->ai_addr, peer->ai_addrlen) < 0) { - perror("failed to connect socket"); - return -1; - } - quiche_config *config = quiche_config_new(0xbabababa); if (config == NULL) { fprintf(stderr, "failed to create config\n"); @@ -240,10 +253,11 @@ int main(int argc, char *argv[]) { } quiche_config_set_application_protos(config, - (uint8_t *) "\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 27); + (uint8_t *) "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 38); quiche_config_set_max_idle_timeout(config, 5000); - quiche_config_set_max_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE); quiche_config_set_initial_max_data(config, 10000000); quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000); quiche_config_set_initial_max_stream_data_uni(config, 1000000); @@ -268,8 +282,9 @@ int main(int argc, char *argv[]) { return -1; } - quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid, - sizeof(scid), config); + quiche_conn *conn = quiche_connect(host, (const uint8_t*) scid, sizeof(scid), + peer->ai_addr, peer->ai_addrlen, config); + if (conn == NULL) { fprintf(stderr, "failed to create connection\n"); return -1; diff --git a/examples/client.rs b/examples/client.rs index 2e427f6..88490aa 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -69,7 +69,6 @@ fn main() { // Create the UDP socket backing the QUIC connection, and register it with // the event loop. let socket = std::net::UdpSocket::bind(bind_addr).unwrap(); - socket.connect(peer_addr).unwrap(); let socket = mio::net::UdpSocket::from_socket(socket).unwrap(); poll.register( @@ -87,11 +86,14 @@ fn main() { config.verify_peer(false); config - .set_application_protos(b"\x05hq-29\x05hq-28\x05hq-27\x08http/0.9") + .set_application_protos( + b"\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", + ) .unwrap(); config.set_max_idle_timeout(5000); - config.set_max_udp_payload_size(MAX_DATAGRAM_SIZE as u64); + config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE); + config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE); config.set_initial_max_data(10_000_000); config.set_initial_max_stream_data_bidi_local(1_000_000); config.set_initial_max_stream_data_bidi_remote(1_000_000); @@ -103,8 +105,11 @@ fn main() { let mut scid = [0; quiche::MAX_CONN_ID_LEN]; SystemRandom::new().fill(&mut scid[..]).unwrap(); + let scid = quiche::ConnectionId::from_ref(&scid); + // Create a QUIC connection and initiate handshake. - let mut conn = quiche::connect(url.domain(), &scid, &mut config).unwrap(); + let mut conn = + quiche::connect(url.domain(), &scid, peer_addr, &mut config).unwrap(); info!( "connecting to {:} from {:} with scid {}", @@ -113,9 +118,9 @@ fn main() { hex_dump(&scid) ); - let write = conn.send(&mut out).expect("initial send failed"); + let (write, send_info) = conn.send(&mut out).expect("initial send failed"); - while let Err(e) = socket.send(&out[..write]) { + while let Err(e) = socket.send_to(&out[..write], &send_info.to) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); continue; @@ -146,7 +151,7 @@ fn main() { break 'read; } - let len = match socket.recv(&mut buf) { + let (len, from) = match socket.recv_from(&mut buf) { Ok(v) => v, Err(e) => { @@ -163,8 +168,10 @@ fn main() { debug!("got {} bytes", len); + let recv_info = quiche::RecvInfo { from }; + // Process potentially coalesced packets. - let read = match conn.recv(&mut buf[..len]) { + let read = match conn.recv(&mut buf[..len], recv_info) { Ok(v) => v, Err(e) => { @@ -228,7 +235,7 @@ fn main() { // Generate outgoing QUIC packets and send them on the UDP socket, until // quiche reports that there are no more packets to be sent. loop { - let write = match conn.send(&mut out) { + let (write, send_info) = match conn.send(&mut out) { Ok(v) => v, Err(quiche::Error::Done) => { @@ -244,7 +251,7 @@ fn main() { }, }; - if let Err(e) = socket.send(&out[..write]) { + if let Err(e) = socket.send_to(&out[..write], &send_info.to) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; diff --git a/examples/http3-client.c b/examples/http3-client.c index 8c75309..6b263ff 100644 --- a/examples/http3-client.c +++ b/examples/http3-client.c @@ -65,8 +65,11 @@ static void debug_log(const char *line, void *argp) { static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { static uint8_t out[MAX_DATAGRAM_SIZE]; + quiche_send_info send_info; + while (1) { - ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out)); + ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out), + &send_info); if (written == QUICHE_ERR_DONE) { fprintf(stderr, "done writing\n"); @@ -78,7 +81,10 @@ static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { return; } - ssize_t sent = send(conn_io->sock, out, written, 0); + ssize_t sent = sendto(conn_io->sock, out, written, 0, + (struct sockaddr *) &send_info.to, + send_info.to_len); + if (sent != written) { perror("failed to send"); return; @@ -109,7 +115,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { static uint8_t buf[65535]; while (1) { - ssize_t read = recv(conn_io->sock, buf, sizeof(buf), 0); + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len = sizeof(peer_addr); + memset(&peer_addr, 0, peer_addr_len); + + ssize_t read = recvfrom(conn_io->sock, buf, sizeof(buf), 0, + (struct sockaddr *) &peer_addr, + &peer_addr_len); if (read < 0) { if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { @@ -121,7 +133,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { return; } - ssize_t done = quiche_conn_recv(conn_io->conn, buf, read); + quiche_recv_info recv_info = { + (struct sockaddr *) &peer_addr, + + peer_addr_len, + }; + + ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info); if (done < 0) { fprintf(stderr, "failed to process packet: %zd\n", done); @@ -239,14 +257,18 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { } case QUICHE_H3_EVENT_DATA: { - ssize_t len = quiche_h3_recv_body(conn_io->http3, - conn_io->conn, s, - buf, sizeof(buf)); - if (len <= 0) { - break; + for (;;) { + ssize_t len = quiche_h3_recv_body(conn_io->http3, + conn_io->conn, s, + buf, sizeof(buf)); + + if (len <= 0) { + break; + } + + printf("%.*s", (int) len, buf); } - printf("%.*s", (int) len, buf); break; } @@ -258,7 +280,7 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { case QUICHE_H3_EVENT_DATAGRAM: break; - + case QUICHE_H3_EVENT_GOAWAY: { fprintf(stderr, "got GOAWAY\n"); break; @@ -322,11 +344,6 @@ int main(int argc, char *argv[]) { return -1; } - if (connect(sock, peer->ai_addr, peer->ai_addrlen) < 0) { - perror("failed to connect socket"); - return -1; - } - quiche_config *config = quiche_config_new(0xbabababa); if (config == NULL) { fprintf(stderr, "failed to create config\n"); @@ -338,7 +355,8 @@ int main(int argc, char *argv[]) { sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); quiche_config_set_max_idle_timeout(config, 5000); - quiche_config_set_max_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE); quiche_config_set_initial_max_data(config, 10000000); quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000); quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000); @@ -366,8 +384,9 @@ int main(int argc, char *argv[]) { return -1; } - quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid, - sizeof(scid), config); + quiche_conn *conn = quiche_connect(host, (const uint8_t*) scid, sizeof(scid), + peer->ai_addr, peer->ai_addrlen, config); + if (conn == NULL) { fprintf(stderr, "failed to create connection\n"); return -1; diff --git a/examples/http3-client.rs b/examples/http3-client.rs index a93d67e..2acb2ca 100644 --- a/examples/http3-client.rs +++ b/examples/http3-client.rs @@ -67,7 +67,6 @@ fn main() { // Create the UDP socket backing the QUIC connection, and register it with // the event loop. let socket = std::net::UdpSocket::bind(bind_addr).unwrap(); - socket.connect(peer_addr).unwrap(); let socket = mio::net::UdpSocket::from_socket(socket).unwrap(); poll.register( @@ -89,7 +88,8 @@ fn main() { .unwrap(); config.set_max_idle_timeout(5000); - config.set_max_udp_payload_size(MAX_DATAGRAM_SIZE as u64); + config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE); + config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE); config.set_initial_max_data(10_000_000); config.set_initial_max_stream_data_bidi_local(1_000_000); config.set_initial_max_stream_data_bidi_remote(1_000_000); @@ -104,8 +104,11 @@ fn main() { let mut scid = [0; quiche::MAX_CONN_ID_LEN]; SystemRandom::new().fill(&mut scid[..]).unwrap(); + let scid = quiche::ConnectionId::from_ref(&scid); + // Create a QUIC connection and initiate handshake. - let mut conn = quiche::connect(url.domain(), &scid, &mut config).unwrap(); + let mut conn = + quiche::connect(url.domain(), &scid, peer_addr, &mut config).unwrap(); info!( "connecting to {:} from {:} with scid {}", @@ -114,9 +117,9 @@ fn main() { hex_dump(&scid) ); - let write = conn.send(&mut out).expect("initial send failed"); + let (write, send_info) = conn.send(&mut out).expect("initial send failed"); - while let Err(e) = socket.send(&out[..write]) { + while let Err(e) = socket.send_to(&out[..write], &send_info.to) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); continue; @@ -138,11 +141,14 @@ fn main() { } let req = vec![ - quiche::h3::Header::new(":method", "GET"), - quiche::h3::Header::new(":scheme", url.scheme()), - quiche::h3::Header::new(":authority", url.host_str().unwrap()), - quiche::h3::Header::new(":path", &path), - quiche::h3::Header::new("user-agent", "quiche"), + quiche::h3::Header::new(b":method", b"GET"), + quiche::h3::Header::new(b":scheme", url.scheme().as_bytes()), + quiche::h3::Header::new( + b":authority", + url.host_str().unwrap().as_bytes(), + ), + quiche::h3::Header::new(b":path", path.as_bytes()), + quiche::h3::Header::new(b"user-agent", b"quiche"), ]; let req_start = std::time::Instant::now(); @@ -166,7 +172,7 @@ fn main() { break 'read; } - let len = match socket.recv(&mut buf) { + let (len, from) = match socket.recv_from(&mut buf) { Ok(v) => v, Err(e) => { @@ -183,8 +189,10 @@ fn main() { debug!("got {} bytes", len); + let recv_info = quiche::RecvInfo { from }; + // Process potentially coalesced packets. - let read = match conn.recv(&mut buf[..len]) { + let read = match conn.recv(&mut buf[..len], recv_info) { Ok(v) => v, Err(e) => { @@ -235,7 +243,7 @@ fn main() { }, Ok((stream_id, quiche::h3::Event::Data)) => { - if let Ok(read) = + while let Ok(read) = http3_conn.recv_body(&mut conn, stream_id, &mut buf) { debug!( @@ -280,7 +288,7 @@ fn main() { // Generate outgoing QUIC packets and send them on the UDP socket, until // quiche reports that there are no more packets to be sent. loop { - let write = match conn.send(&mut out) { + let (write, send_info) = match conn.send(&mut out) { Ok(v) => v, Err(quiche::Error::Done) => { @@ -296,7 +304,7 @@ fn main() { }, }; - if let Err(e) = socket.send(&out[..write]) { + if let Err(e) = socket.send_to(&out[..write], &send_info.to) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; diff --git a/examples/http3-server.c b/examples/http3-server.c index b6b3041..73c29ae 100644 --- a/examples/http3-server.c +++ b/examples/http3-server.c @@ -89,8 +89,11 @@ static void debug_log(const char *line, void *argp) { static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { static uint8_t out[MAX_DATAGRAM_SIZE]; + quiche_send_info send_info; + while (1) { - ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out)); + ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out), + &send_info); if (written == QUICHE_ERR_DONE) { fprintf(stderr, "done writing\n"); @@ -173,7 +176,9 @@ static uint8_t *gen_cid(uint8_t *cid, size_t cid_len) { } static struct conn_io *create_conn(uint8_t *scid, size_t scid_len, - uint8_t *odcid, size_t odcid_len) { + uint8_t *odcid, size_t odcid_len, + struct sockaddr_storage *peer_addr, + socklen_t peer_addr_len) { struct conn_io *conn_io = calloc(1, sizeof(*conn_io)); if (conn_io == NULL) { fprintf(stderr, "failed to allocate connection IO\n"); @@ -187,7 +192,11 @@ static struct conn_io *create_conn(uint8_t *scid, size_t scid_len, memcpy(conn_io->cid, scid, LOCAL_CONN_ID_LEN); quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN, - odcid, odcid_len, config); + odcid, odcid_len, + (struct sockaddr *) peer_addr, + peer_addr_len, + config); + if (conn == NULL) { fprintf(stderr, "failed to create connection\n"); return NULL; @@ -196,6 +205,9 @@ static struct conn_io *create_conn(uint8_t *scid, size_t scid_len, conn_io->sock = conns->sock; conn_io->conn = conn; + memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len); + conn_io->peer_addr_len = peer_addr_len; + ev_init(&conn_io->timer, timeout_cb); conn_io->timer.data = conn_io; @@ -334,16 +346,21 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { continue; } - conn_io = create_conn(dcid, dcid_len, odcid, odcid_len); + conn_io = create_conn(dcid, dcid_len, odcid, odcid_len, + &peer_addr, peer_addr_len); + if (conn_io == NULL) { continue; } - - memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len); - conn_io->peer_addr_len = peer_addr_len; } - ssize_t done = quiche_conn_recv(conn_io->conn, buf, read); + quiche_recv_info recv_info = { + (struct sockaddr *) &peer_addr, + + peer_addr_len, + }; + + ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info); if (done < 0) { fprintf(stderr, "failed to process packet: %zd\n", done); @@ -532,7 +549,8 @@ int main(int argc, char *argv[]) { sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); quiche_config_set_max_idle_timeout(config, 5000); - quiche_config_set_max_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE); quiche_config_set_initial_max_data(config, 10000000); quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000); quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000); diff --git a/examples/http3-server.rs b/examples/http3-server.rs index 4c41cbb..e84d1ea 100644 --- a/examples/http3-server.rs +++ b/examples/http3-server.rs @@ -53,7 +53,7 @@ struct Client { partial_responses: HashMap<u64, PartialResponse>, } -type ClientMap = HashMap<Vec<u8>, (net::SocketAddr, Client)>; +type ClientMap = HashMap<quiche::ConnectionId<'static>, Client>; fn main() { let mut buf = [0; 65535]; @@ -100,7 +100,8 @@ fn main() { .unwrap(); config.set_max_idle_timeout(5000); - config.set_max_udp_payload_size(MAX_DATAGRAM_SIZE as u64); + config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE); + config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE); config.set_initial_max_data(10_000_000); config.set_initial_max_stream_data_bidi_local(1_000_000); config.set_initial_max_stream_data_bidi_remote(1_000_000); @@ -122,8 +123,7 @@ fn main() { // Find the shorter timeout from all the active connections. // // TODO: use event loop that properly supports timers - let timeout = - clients.values().filter_map(|(_, c)| c.conn.timeout()).min(); + let timeout = clients.values().filter_map(|c| c.conn.timeout()).min(); poll.poll(&mut events, timeout).unwrap(); @@ -136,12 +136,12 @@ fn main() { if events.is_empty() { debug!("timed out"); - clients.values_mut().for_each(|(_, c)| c.conn.on_timeout()); + clients.values_mut().for_each(|c| c.conn.on_timeout()); break 'read; } - let (len, src) = match socket.recv_from(&mut buf) { + let (len, from) = match socket.recv_from(&mut buf) { Ok(v) => v, Err(e) => { @@ -177,11 +177,12 @@ fn main() { let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid); let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN]; + let conn_id = conn_id.to_vec().into(); // Lookup a connection based on the packet's connection ID. If there // is no connection matching, create a new one. - let (_, client) = if !clients.contains_key(&hdr.dcid) && - !clients.contains_key(conn_id) + let client = if !clients.contains_key(&hdr.dcid) && + !clients.contains_key(&conn_id) { if hdr.ty != quiche::Type::Initial { error!("Packet is not Initial"); @@ -197,7 +198,7 @@ fn main() { let out = &out[..len]; - if let Err(e) = socket.send_to(out, &src) { + if let Err(e) = socket.send_to(out, &from) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; @@ -211,6 +212,8 @@ fn main() { let mut scid = [0; quiche::MAX_CONN_ID_LEN]; scid.copy_from_slice(&conn_id); + let scid = quiche::ConnectionId::from_ref(&scid); + // Token is always present in Initial packets. let token = hdr.token.as_ref().unwrap(); @@ -218,7 +221,7 @@ fn main() { if token.is_empty() { warn!("Doing stateless retry"); - let new_token = mint_token(&hdr, &src); + let new_token = mint_token(&hdr, &from); let len = quiche::retry( &hdr.scid, @@ -232,7 +235,7 @@ fn main() { let out = &out[..len]; - if let Err(e) = socket.send_to(out, &src) { + if let Err(e) = socket.send_to(out, &from) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; @@ -243,11 +246,11 @@ fn main() { continue 'read; } - let odcid = validate_token(&src, token); + let odcid = validate_token(&from, token); // The token was not valid, meaning the retry failed, so // drop the packet. - if odcid == None { + if odcid.is_none() { error!("Invalid address validation token"); continue 'read; } @@ -257,17 +260,15 @@ fn main() { continue 'read; } - // Reuse the source connection ID we sent in the Retry - // packet, instead of changing it again. - scid.copy_from_slice(&hdr.dcid); + // Reuse the source connection ID we sent in the Retry packet, + // instead of changing it again. + let scid = hdr.dcid.clone(); - debug!( - "New connection: dcid={} scid={}", - hex_dump(&hdr.dcid), - hex_dump(&scid) - ); + debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid); - let conn = quiche::accept(&scid, odcid, &mut config).unwrap(); + let conn = + quiche::accept(&scid, odcid.as_ref(), from, &mut config) + .unwrap(); let client = Client { conn, @@ -275,19 +276,21 @@ fn main() { partial_responses: HashMap::new(), }; - clients.insert(scid.to_vec(), (src, client)); + clients.insert(scid.clone(), client); - clients.get_mut(&scid[..]).unwrap() + clients.get_mut(&scid).unwrap() } else { match clients.get_mut(&hdr.dcid) { Some(v) => v, - None => clients.get_mut(conn_id).unwrap(), + None => clients.get_mut(&conn_id).unwrap(), } }; + let recv_info = quiche::RecvInfo { from }; + // Process potentially coalesced packets. - let read = match client.conn.recv(pkt_buf) { + let read = match client.conn.recv(pkt_buf, recv_info) { Ok(v) => v, Err(e) => { @@ -382,9 +385,9 @@ fn main() { // Generate outgoing QUIC packets for all active connections and send // them on the UDP socket, until quiche reports that there are no more // packets to be sent. - for (peer, client) in clients.values_mut() { + for client in clients.values_mut() { loop { - let write = match client.conn.send(&mut out) { + let (write, send_info) = match client.conn.send(&mut out) { Ok(v) => v, Err(quiche::Error::Done) => { @@ -400,8 +403,7 @@ fn main() { }, }; - // TODO: coalesce packets. - if let Err(e) = socket.send_to(&out[..write], &peer) { + if let Err(e) = socket.send_to(&out[..write], &send_info.to) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; @@ -415,7 +417,7 @@ fn main() { } // Garbage collect closed connections. - clients.retain(|_, (_, ref mut c)| { + clients.retain(|_, ref mut c| { debug!("Collecting garbage"); if c.conn.is_closed() { @@ -464,7 +466,7 @@ fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> { /// authenticate of the token. *It should not be used in production system*. fn validate_token<'a>( src: &net::SocketAddr, token: &'a [u8], -) -> Option<&'a [u8]> { +) -> Option<quiche::ConnectionId<'a>> { if token.len() < 6 { return None; } @@ -484,9 +486,7 @@ fn validate_token<'a>( return None; } - let token = &token[addr.len()..]; - - Some(&token[..]) + Some(quiche::ConnectionId::from_ref(&token[addr.len()..])) } /// Handles incoming HTTP/3 requests. @@ -535,6 +535,8 @@ fn handle_request( let written = match http3_conn.send_body(conn, stream_id, &body, true) { Ok(v) => v, + Err(quiche::h3::Error::Done) => 0, + Err(e) => { error!("{} stream send failed {:?}", conn.trace_id(), e); return; @@ -558,25 +560,24 @@ fn build_response( ) -> (Vec<quiche::h3::Header>, Vec<u8>) { let mut file_path = std::path::PathBuf::from(root); let mut path = std::path::Path::new(""); - let mut method = ""; + let mut method = None; // Look for the request's path and method. for hdr in request { match hdr.name() { - ":path" => { - path = std::path::Path::new(hdr.value()); - }, + b":path" => + path = std::path::Path::new( + std::str::from_utf8(hdr.value()).unwrap(), + ), - ":method" => { - method = hdr.value(); - }, + b":method" => method = Some(hdr.value()), _ => (), } } let (status, body) = match method { - "GET" => { + Some(b"GET") => { for c in path.components() { if let std::path::Component::Normal(v) = c { file_path.push(v) @@ -594,9 +595,12 @@ fn build_response( }; let headers = vec![ - quiche::h3::Header::new(":status", &status.to_string()), - quiche::h3::Header::new("server", "quiche"), - quiche::h3::Header::new("content-length", &body.len().to_string()), + quiche::h3::Header::new(b":status", status.to_string().as_bytes()), + quiche::h3::Header::new(b"server", b"quiche"), + quiche::h3::Header::new( + b"content-length", + body.len().to_string().as_bytes(), + ), ]; (headers, body) @@ -637,7 +641,11 @@ fn handle_writable(client: &mut Client, stream_id: u64) { let written = match http3_conn.send_body(conn, stream_id, body, true) { Ok(v) => v, + Err(quiche::h3::Error::Done) => 0, + Err(e) => { + client.partial_responses.remove(&stream_id); + error!("{} stream send failed {:?}", conn.trace_id(), e); return; }, @@ -649,9 +657,3 @@ fn handle_writable(client: &mut Client, stream_id: u64) { client.partial_responses.remove(&stream_id); } } - -fn hex_dump(buf: &[u8]) -> String { - let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect(); - - vec.join("") -} diff --git a/examples/qpack-decode.rs b/examples/qpack-decode.rs index 8468a85..d2aaaa5 100644 --- a/examples/qpack-decode.rs +++ b/examples/qpack-decode.rs @@ -77,7 +77,9 @@ fn main() { } for hdr in dec.decode(&data[..len], std::u64::MAX).unwrap() { - println!("{}\t{}", hdr.name(), hdr.value()); + let name = std::str::from_utf8(hdr.name()).unwrap(); + let value = std::str::from_utf8(hdr.value()).unwrap(); + println!("{}\t{}", name, value); } println!(); diff --git a/examples/qpack-encode.rs b/examples/qpack-encode.rs index e381227..5215fe4 100644 --- a/examples/qpack-encode.rs +++ b/examples/qpack-encode.rs @@ -83,6 +83,6 @@ fn main() { let name = line.split('\t').next().unwrap(); let value = line.split('\t').last().unwrap(); - headers.push(h3::Header::new(name, value)); + headers.push(h3::Header::new(name.as_bytes(), value.as_bytes())); } } diff --git a/examples/server.c b/examples/server.c index 025e1d5..a97250f 100644 --- a/examples/server.c +++ b/examples/server.c @@ -86,8 +86,11 @@ static void debug_log(const char *line, void *argp) { static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { static uint8_t out[MAX_DATAGRAM_SIZE]; + quiche_send_info send_info; + while (1) { - ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out)); + ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out), + &send_info); if (written == QUICHE_ERR_DONE) { fprintf(stderr, "done writing\n"); @@ -100,8 +103,9 @@ static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { } ssize_t sent = sendto(conn_io->sock, out, written, 0, - (struct sockaddr *) &conn_io->peer_addr, - conn_io->peer_addr_len); + (struct sockaddr *) &send_info.to, + send_info.to_len); + if (sent != written) { perror("failed to send"); return; @@ -169,18 +173,28 @@ static uint8_t *gen_cid(uint8_t *cid, size_t cid_len) { return cid; } -static struct conn_io *create_conn(uint8_t *dcid, size_t dcid_len, uint8_t *odcid, - size_t odcid_len) { - struct conn_io *conn_io = malloc(sizeof(*conn_io)); +static struct conn_io *create_conn(uint8_t *scid, size_t scid_len, + uint8_t *odcid, size_t odcid_len, + struct sockaddr_storage *peer_addr, + socklen_t peer_addr_len) { + struct conn_io *conn_io = calloc(1, sizeof(*conn_io)); if (conn_io == NULL) { fprintf(stderr, "failed to allocate connection IO\n"); return NULL; } - memcpy(conn_io->cid, dcid, LOCAL_CONN_ID_LEN); + if (scid_len != LOCAL_CONN_ID_LEN) { + fprintf(stderr, "failed, scid length too short\n"); + } + + memcpy(conn_io->cid, scid, LOCAL_CONN_ID_LEN); quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN, - odcid, odcid_len, config); + odcid, odcid_len, + (struct sockaddr *) peer_addr, + peer_addr_len, + config); + if (conn == NULL) { fprintf(stderr, "failed to create connection\n"); return NULL; @@ -189,6 +203,9 @@ static struct conn_io *create_conn(uint8_t *dcid, size_t dcid_len, uint8_t *odci conn_io->sock = conns->sock; conn_io->conn = conn; + memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len); + conn_io->peer_addr_len = peer_addr_len; + ev_init(&conn_io->timer, timeout_cb); conn_io->timer.data = conn_io; @@ -318,16 +335,21 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { continue; } - conn_io = create_conn(dcid, dcid_len, odcid, odcid_len); + conn_io = create_conn(dcid, dcid_len, odcid, odcid_len, + &peer_addr, peer_addr_len); + if (conn_io == NULL) { continue; } - - memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len); - conn_io->peer_addr_len = peer_addr_len; } - ssize_t done = quiche_conn_recv(conn_io->conn, buf, read); + quiche_recv_info recv_info = { + (struct sockaddr *) &peer_addr, + + peer_addr_len, + }; + + ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info); if (done < 0) { fprintf(stderr, "failed to process packet: %zd\n", done); @@ -451,10 +473,11 @@ int main(int argc, char *argv[]) { quiche_config_load_priv_key_from_pem_file(config, "./cert.key"); quiche_config_set_application_protos(config, - (uint8_t *) "\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 27); + (uint8_t *) "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 38); quiche_config_set_max_idle_timeout(config, 5000); - quiche_config_set_max_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE); + quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE); quiche_config_set_initial_max_data(config, 10000000); quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000); quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000); diff --git a/examples/server.rs b/examples/server.rs index 8213d95..90f0102 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -47,7 +47,7 @@ struct Client { partial_responses: HashMap<u64, PartialResponse>, } -type ClientMap = HashMap<Vec<u8>, (net::SocketAddr, Client)>; +type ClientMap = HashMap<quiche::ConnectionId<'static>, Client>; fn main() { let mut buf = [0; 65535]; @@ -90,11 +90,14 @@ fn main() { .unwrap(); config - .set_application_protos(b"\x05hq-29\x05hq-28\x05hq-27\x08http/0.9") + .set_application_protos( + b"\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", + ) .unwrap(); config.set_max_idle_timeout(5000); - config.set_max_udp_payload_size(MAX_DATAGRAM_SIZE as u64); + config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE); + config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE); config.set_initial_max_data(10_000_000); config.set_initial_max_stream_data_bidi_local(1_000_000); config.set_initial_max_stream_data_bidi_remote(1_000_000); @@ -114,8 +117,7 @@ fn main() { // Find the shorter timeout from all the active connections. // // TODO: use event loop that properly supports timers - let timeout = - clients.values().filter_map(|(_, c)| c.conn.timeout()).min(); + let timeout = clients.values().filter_map(|c| c.conn.timeout()).min(); poll.poll(&mut events, timeout).unwrap(); @@ -128,12 +130,12 @@ fn main() { if events.is_empty() { debug!("timed out"); - clients.values_mut().for_each(|(_, c)| c.conn.on_timeout()); + clients.values_mut().for_each(|c| c.conn.on_timeout()); break 'read; } - let (len, src) = match socket.recv_from(&mut buf) { + let (len, from) = match socket.recv_from(&mut buf) { Ok(v) => v, Err(e) => { @@ -169,11 +171,12 @@ fn main() { let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid); let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN]; + let conn_id = conn_id.to_vec().into(); // Lookup a connection based on the packet's connection ID. If there // is no connection matching, create a new one. - let (_, client) = if !clients.contains_key(&hdr.dcid) && - !clients.contains_key(conn_id) + let client = if !clients.contains_key(&hdr.dcid) && + !clients.contains_key(&conn_id) { if hdr.ty != quiche::Type::Initial { error!("Packet is not Initial"); @@ -189,7 +192,7 @@ fn main() { let out = &out[..len]; - if let Err(e) = socket.send_to(out, &src) { + if let Err(e) = socket.send_to(out, &from) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; @@ -203,6 +206,8 @@ fn main() { let mut scid = [0; quiche::MAX_CONN_ID_LEN]; scid.copy_from_slice(&conn_id); + let scid = quiche::ConnectionId::from_ref(&scid); + // Token is always present in Initial packets. let token = hdr.token.as_ref().unwrap(); @@ -210,7 +215,7 @@ fn main() { if token.is_empty() { warn!("Doing stateless retry"); - let new_token = mint_token(&hdr, &src); + let new_token = mint_token(&hdr, &from); let len = quiche::retry( &hdr.scid, @@ -224,7 +229,7 @@ fn main() { let out = &out[..len]; - if let Err(e) = socket.send_to(out, &src) { + if let Err(e) = socket.send_to(out, &from) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; @@ -235,11 +240,11 @@ fn main() { continue 'read; } - let odcid = validate_token(&src, token); + let odcid = validate_token(&from, token); // The token was not valid, meaning the retry failed, so // drop the packet. - if odcid == None { + if odcid.is_none() { error!("Invalid address validation token"); continue 'read; } @@ -249,36 +254,36 @@ fn main() { continue 'read; } - // Reuse the source connection ID we sent in the Retry - // packet, instead of changing it again. - scid.copy_from_slice(&hdr.dcid); + // Reuse the source connection ID we sent in the Retry packet, + // instead of changing it again. + let scid = hdr.dcid.clone(); - debug!( - "New connection: dcid={} scid={}", - hex_dump(&hdr.dcid), - hex_dump(&scid) - ); + debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid); - let conn = quiche::accept(&scid, odcid, &mut config).unwrap(); + let conn = + quiche::accept(&scid, odcid.as_ref(), from, &mut config) + .unwrap(); let client = Client { conn, partial_responses: HashMap::new(), }; - clients.insert(scid.to_vec(), (src, client)); + clients.insert(scid.clone(), client); - clients.get_mut(&scid[..]).unwrap() + clients.get_mut(&scid).unwrap() } else { match clients.get_mut(&hdr.dcid) { Some(v) => v, - None => clients.get_mut(conn_id).unwrap(), + None => clients.get_mut(&conn_id).unwrap(), } }; + let recv_info = quiche::RecvInfo { from }; + // Process potentially coalesced packets. - let read = match client.conn.recv(pkt_buf) { + let read = match client.conn.recv(pkt_buf, recv_info) { Ok(v) => v, Err(e) => { @@ -325,9 +330,9 @@ fn main() { // Generate outgoing QUIC packets for all active connections and send // them on the UDP socket, until quiche reports that there are no more // packets to be sent. - for (peer, client) in clients.values_mut() { + for client in clients.values_mut() { loop { - let write = match client.conn.send(&mut out) { + let (write, send_info) = match client.conn.send(&mut out) { Ok(v) => v, Err(quiche::Error::Done) => { @@ -343,8 +348,7 @@ fn main() { }, }; - // TODO: coalesce packets. - if let Err(e) = socket.send_to(&out[..write], &peer) { + if let Err(e) = socket.send_to(&out[..write], &send_info.to) { if e.kind() == std::io::ErrorKind::WouldBlock { debug!("send() would block"); break; @@ -358,7 +362,7 @@ fn main() { } // Garbage collect closed connections. - clients.retain(|_, (_, ref mut c)| { + clients.retain(|_, ref mut c| { debug!("Collecting garbage"); if c.conn.is_closed() { @@ -407,7 +411,7 @@ fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> { /// authenticate of the token. *It should not be used in production system*. fn validate_token<'a>( src: &net::SocketAddr, token: &'a [u8], -) -> Option<&'a [u8]> { +) -> Option<quiche::ConnectionId<'a>> { if token.len() < 6 { return None; } @@ -427,9 +431,7 @@ fn validate_token<'a>( return None; } - let token = &token[addr.len()..]; - - Some(&token[..]) + Some(quiche::ConnectionId::from_ref(&token[addr.len()..])) } /// Handles incoming HTTP/0.9 requests. @@ -503,6 +505,8 @@ fn handle_writable(client: &mut Client, stream_id: u64) { Err(quiche::Error::Done) => 0, Err(e) => { + client.partial_responses.remove(&stream_id); + error!("{} stream send failed {:?}", conn.trace_id(), e); return; }, @@ -514,9 +518,3 @@ fn handle_writable(client: &mut Client, stream_id: u64) { client.partial_responses.remove(&stream_id); } } - -fn hex_dump(buf: &[u8]) -> String { - let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect(); - - vec.join("") -} |