diff --git a/doc/programmers-guide.rst b/doc/programmers-guide.rst index 8a4bed47b485ce51b674d656d7745a9eb4533a91..4ba4f69a96c2b105e4b40d87802998984588c6c3 100644 --- a/doc/programmers-guide.rst +++ b/doc/programmers-guide.rst @@ -177,8 +177,8 @@ client_*_traffic_secret. After Handshake key is available, set AEAD overhead (tag length) using `ngtcp2_conn_set_aead_overhead()` function. -`ngtcp2_conn_write_handshake()` initiates QUIC handshake. The Initial -keys must be installed before calling this function. +`ngtcp2_conn_write_pkt()` initiates QUIC handshake. The Initial keys +must be installed before calling this function. For client application, it first calls ``ngtcp2_conn_callbacks.client_initial`` callback. The callback must @@ -198,7 +198,7 @@ After negotiated Handshake keys are available, negotiated cipher suites. If ChaCha20 based cipher suite is negotiated, ChaCha20 is used to protect packet header. -`ngtcp2_conn_read_handshake()` reads QUIC handshake packets. +`ngtcp2_conn_read_pkt()` reads QUIC handshake packets. For server application, it first calls ``ngtcp2_conn_callbacks.recv_client_initial`` callback. The callback @@ -218,23 +218,23 @@ acknowledges TLS messages, ``ngtcp2_conn_callbacks.acked_crypto_offset`` callback is called. The application can throw away data acknowledged. -`ngtcp2_conn_read_handshake()` and `ngtcp2_conn_write_handshake()` -should be called until `ngtcp2_conn_get_handshake_completed()` returns -nonzero which means QUIC handshake has completed. +`ngtcp2_conn_read_pkt()` and `ngtcp2_conn_write_pkt()` performs QUIC +handshake until `ngtcp2_conn_get_handshake_completed()` returns +nonzero which means QUIC handshake has completed. They can be used +for post-handshake data transfer as well. To send stream data, use +`ngtcp2_conn_writev_stream()`. 0RTT data transmission ---------------------- In order for client to send 0RTT data, it should use -`ngtcp2_conn_client_write_handshake()` function instead of -`ngtcp2_conn_write_handshake()`. -`ngtcp2_conn_client_write_handshake()` accepts 0RTT data to send. +`ngtcp2_conn_writev_stream()` function. Client application has to load resumed TLS session. It also has to set the remembered transport parameter using `ngtcp2_conn_set_early_remote_transport_params()` function. -Before calling `ngtcp2_conn_client_write_handshake()`, client +Before calling `ngtcp2_conn_client_writev_stream()`, client application has to open stream to send data using `ngtcp2_conn_open_bidi_stream()` (or `ngtcp2_conn_open_uni_stream()` for unidirectional stream). @@ -268,28 +268,27 @@ When timer fires, it has to call some API functions. If the current timestamp is equal to or larger than the value returned from `ngtcp2_conn_loss_detection_expiry()`, it has to call `ngtcp2_conn_on_loss_detection_timer()` and `ngtcp2_conn_write_pkt()` -(or `ngtcp2_conn_write_handshake()` if handshake has not completed -yet). If the current timestamp is equal to or larger than the value -returned from `ngtcp2_conn_ack_delay_expiry()`, it has to call +(or `ngtcp2_conn_writev_stream()`). If the current timestamp is equal +to or larger than the value returned from +`ngtcp2_conn_ack_delay_expiry()`, it has to call `ngtcp2_conn_cancel_expired_ack_delay_timer()` and -`ngtcp2_conn_write_pkt()` (or `ngtcp2_conn_write_handshake()` if -handshake has not completed yet). After calling these functions, new -expiry will be set. The application should call -`ngtcp2_conn_get_expiry()` to restart timer. +`ngtcp2_conn_write_pkt()` (or `ngtcp2_conn_writev_stream()`). After +calling these functions, new expiry will be set. The application +should call `ngtcp2_conn_get_expiry()` to restart timer. After QUIC handshake -------------------- -After QUIC handshake completed, call `ngtcp2_conn_read_pkt()` to read +After QUIC handshake completed, `ngtcp2_conn_read_pkt()` can read incoming QUIC packets. To write QUIC packets, call `ngtcp2_conn_write_pkt()`. In order to send stream data, the application has to first open a stream. Use `ngtcp2_conn_open_bidi_stream()` to open bidirectional stream. For unidirectional stream, call -`ngtcp2_conn_open_uni_stream()`. Call `ngtcp2_conn_write_stream()` to -send stream data. +`ngtcp2_conn_open_uni_stream()`. Call `ngtcp2_conn_writev_stream()` +to send stream data. Closing connection ------------------ diff --git a/examples/client.cc b/examples/client.cc index 7cf29c3c6db1008c108048810ee7e5a9e17c29aa..8271f3736a0619b979c557f80c9a336a81f84b7a 100644 --- a/examples/client.cc +++ b/examples/client.cc @@ -1273,102 +1273,19 @@ int Client::feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data, auto path = ngtcp2_path{ {local_addr_.len, reinterpret_cast<uint8_t *>(&local_addr_.su)}, {salen, const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(sa))}}; - if (ngtcp2_conn_get_handshake_completed(conn_)) { - rv = ngtcp2_conn_read_pkt(conn_, &path, data, datalen, - util::timestamp(loop_)); - if (rv != 0) { - std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl; - if (!last_error_.code) { - last_error_ = quicErrorTransport(rv); - } - disconnect(); - return -1; + rv = + ngtcp2_conn_read_pkt(conn_, &path, data, datalen, util::timestamp(loop_)); + if (rv != 0) { + std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl; + if (!last_error_.code) { + last_error_ = quicErrorTransport(rv); } - return 0; - } - - return do_handshake(&path, data, datalen); -} - -int Client::do_handshake_read_once(const ngtcp2_path *path, const uint8_t *data, - size_t datalen) { - auto rv = ngtcp2_conn_read_handshake(conn_, path, data, datalen, - util::timestamp(loop_)); - if (rv < 0) { - std::cerr << "ngtcp2_conn_read_handshake: " << ngtcp2_strerror(rv) - << std::endl; - last_error_ = quicErrorTransport(rv); disconnect(); return -1; } - return 0; } -ssize_t Client::do_handshake_write_once() { - auto nwrite = ngtcp2_conn_write_handshake(conn_, sendbuf_.wpos(), max_pktlen_, - util::timestamp(loop_)); - if (nwrite < 0) { - std::cerr << "ngtcp2_conn_write_handshake: " << ngtcp2_strerror(nwrite) - << std::endl; - last_error_ = quicErrorTransport(nwrite); - disconnect(); - return -1; - } - - if (nwrite == 0) { - return 0; - } - - sendbuf_.push(nwrite); - - auto rv = send_packet(); - if (rv == NETWORK_ERR_SEND_BLOCKED) { - schedule_retransmit(); - return rv; - } - if (rv != NETWORK_ERR_OK) { - return rv; - } - - return nwrite; -} - -int Client::do_handshake(const ngtcp2_path *path, const uint8_t *data, - size_t datalen) { - ssize_t nwrite; - - if (sendbuf_.size() > 0) { - auto rv = send_packet(); - if (rv != NETWORK_ERR_OK) { - return rv; - } - } - - if (datalen) { - auto rv = do_handshake_read_once(path, data, datalen); - if (rv != 0) { - return rv; - } - } - - // For 0-RTT - auto rv = write_0rtt_streams(); - if (rv != 0) { - return rv; - } - - for (;;) { - nwrite = do_handshake_write_once(); - if (nwrite < 0) { - return nwrite; - } - if (nwrite == 0) { - return 0; - } - } -} - int Client::on_read() { std::array<uint8_t, 65536> buf; sockaddr_union su; @@ -1440,49 +1357,12 @@ int Client::on_write(bool retransmit) { } } - if (!ngtcp2_conn_get_handshake_completed(conn_)) { - auto rv = do_handshake(nullptr, nullptr, 0); - schedule_retransmit(); - return rv; - } - - PathStorage path; - - for (;;) { - auto n = ngtcp2_conn_write_pkt(conn_, &path.path, sendbuf_.wpos(), - max_pktlen_, util::timestamp(loop_)); - if (n < 0) { - std::cerr << "ngtcp2_conn_write_pkt: " << ngtcp2_strerror(n) << std::endl; - last_error_ = quicErrorTransport(n); - disconnect(); - return -1; - } - if (n == 0) { - break; - } - - sendbuf_.push(n); - - update_remote_addr(&path.path.remote); - - auto rv = send_packet(); + auto rv = write_streams(); + if (rv != 0) { if (rv == NETWORK_ERR_SEND_BLOCKED) { schedule_retransmit(); - return rv; - } - if (rv != NETWORK_ERR_OK) { - return rv; - } - } - - if (!retransmit) { - auto rv = write_streams(); - if (rv != 0) { - if (rv == NETWORK_ERR_SEND_BLOCKED) { - schedule_retransmit(); - } - return rv; } + return rv; } schedule_retransmit(); @@ -1493,146 +1373,60 @@ int Client::write_streams() { std::array<nghttp3_vec, 16> vec; int rv; - if (ngtcp2_conn_get_max_data_left(conn_) == 0) { - return 0; - } - for (;;) { int64_t stream_id; int fin; - auto sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin, - vec.data(), vec.size()); - if (sveccnt < 0) { - std::cerr << "nghttp3_conn_writev_stream: " << nghttp3_strerror(sveccnt) - << std::endl; - last_error_ = quicErrorApplication(sveccnt); - disconnect(); - return -1; - } + ssize_t sveccnt = 0; - if (sveccnt == 0) { - break; - } - - ssize_t ndatalen; - PathStorage path; - auto v = vec.data(); - auto vcnt = static_cast<size_t>(sveccnt); - for (;;) { - auto nwrite = ngtcp2_conn_writev_stream( - conn_, &path.path, sendbuf_.wpos(), max_pktlen_, &ndatalen, stream_id, - fin, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, - util::timestamp(loop_)); - if (nwrite < 0) { - auto should_break = false; - switch (nwrite) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: - if (ngtcp2_conn_get_max_data_left(conn_) == 0) { - return 0; - } - - rv = nghttp3_conn_block_stream(httpconn_, stream_id); - if (rv != 0) { - std::cerr << "nghttp3_conn_block_stream: " << nghttp3_strerror(rv) - << std::endl; - last_error_ = quicErrorApplication(rv); - disconnect(); - return -1; - } - should_break = true; - break; - case NGTCP2_ERR_EARLY_DATA_REJECTED: - case NGTCP2_ERR_STREAM_SHUT_WR: - case NGTCP2_ERR_STREAM_NOT_FOUND: // This means that stream is - // closed. - should_break = true; - break; - } - - if (should_break) { - break; - } - - std::cerr << "ngtcp2_conn_write_stream: " << ngtcp2_strerror(nwrite) + if (httpconn_ && ngtcp2_conn_get_max_data_left(conn_)) { + sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin, + vec.data(), vec.size()); + if (sveccnt < 0) { + std::cerr << "nghttp3_conn_writev_stream: " << nghttp3_strerror(sveccnt) << std::endl; - last_error_ = quicErrorTransport(nwrite); + last_error_ = quicErrorApplication(sveccnt); disconnect(); return -1; } + } - if (nwrite == 0) { - // We are congestion limited. - return 0; - } - - sendbuf_.push(nwrite); - - if (ndatalen > 0) { - rv = nghttp3_conn_add_write_offset(httpconn_, stream_id, ndatalen); - if (rv != 0) { - std::cerr << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv) + ssize_t ndatalen; + PathStorage path; + if (sveccnt == 0) { + for (;;) { + auto nwrite = + ngtcp2_conn_write_pkt(conn_, &path.path, sendbuf_.wpos(), + max_pktlen_, util::timestamp(loop_)); + if (nwrite < 0) { + std::cerr << "ngtcp2_conn_write_pkt: " << ngtcp2_strerror(nwrite) << std::endl; - last_error_ = quicErrorApplication(rv); + last_error_ = quicErrorTransport(nwrite); disconnect(); return -1; } + if (nwrite == 0) { + return 0; + } + sendbuf_.push(nwrite); - nghttp3_vec_consume(&v, &vcnt, ndatalen); - } - - update_remote_addr(&path.path.remote); + update_remote_addr(&path.path.remote); - auto rv = send_packet(); - if (rv != NETWORK_ERR_OK) { - return rv; - } + auto rv = send_packet(); + if (rv != NETWORK_ERR_OK) { + return rv; + } - if (nghttp3_vec_empty(v, vcnt)) { - break; + return 0; } } - } - - return 0; -} - -int Client::write_0rtt_streams() { - if (!httpconn_) { - return 0; - } - std::array<nghttp3_vec, 16> vec; - int rv; - - if (ngtcp2_conn_get_max_data_left(conn_) == 0) { - return 0; - } - - for (;;) { - int64_t stream_id; - int fin; - - auto sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin, - vec.data(), vec.size()); - if (sveccnt < 0) { - std::cerr << "nghttp3_conn_writev_stream: " << nghttp3_strerror(sveccnt) - << std::endl; - last_error_ = quicErrorApplication(sveccnt); - disconnect(); - return -1; - } - - if (sveccnt == 0) { - break; - } - - ssize_t ndatalen; auto v = vec.data(); auto vcnt = static_cast<size_t>(sveccnt); for (;;) { - auto nwrite = ngtcp2_conn_client_write_handshake( - conn_, sendbuf_.wpos(), max_pktlen_, &ndatalen, stream_id, fin, + auto nwrite = ngtcp2_conn_writev_stream( + conn_, &path.path, sendbuf_.wpos(), max_pktlen_, &ndatalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, fin, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, util::timestamp(loop_)); if (nwrite < 0) { @@ -1659,6 +1453,18 @@ int Client::write_0rtt_streams() { // closed. should_break = true; break; + case NGTCP2_ERR_WRITE_STREAM_MORE: + assert(ndatalen > 0); + rv = nghttp3_conn_add_write_offset(httpconn_, stream_id, ndatalen); + if (rv != 0) { + std::cerr << "nghttp3_conn_add_write_offset: " + << nghttp3_strerror(rv) << std::endl; + last_error_ = quicErrorApplication(rv); + disconnect(); + return -1; + } + should_break = true; + break; } if (should_break) { @@ -1692,6 +1498,8 @@ int Client::write_0rtt_streams() { nghttp3_vec_consume(&v, &vcnt, ndatalen); } + update_remote_addr(&path.path.remote); + auto rv = send_packet(); if (rv != NETWORK_ERR_OK) { return rv; @@ -1702,8 +1510,6 @@ int Client::write_0rtt_streams() { } } } - - return 0; } void Client::schedule_retransmit() { @@ -2887,7 +2693,6 @@ SSL_CTX *create_ssl_ctx() { namespace { int run(Client &c, const char *addr, const char *port) { Address remote_addr, local_addr; - ssize_t nwrite; auto fd = create_sock(remote_addr, addr, port); if (fd == -1) { @@ -2914,19 +2719,12 @@ int run(Client &c, const char *addr, const char *port) { } } - // For 0-RTT - auto rv = c.write_0rtt_streams(); + // TODO Do we need this ? + auto rv = c.on_write(); if (rv != 0) { return rv; } - nwrite = c.do_handshake_write_once(); - if (nwrite < 0) { - return nwrite; - } - - c.schedule_retransmit(); - ev_run(EV_DEFAULT, 0); return 0; diff --git a/examples/client.h b/examples/client.h index 336aca42cd2daab848bdfdcab49bef48d2512ae3..5ad71d9081e2ebc728f061a3620d12dc5ad40ae0 100644 --- a/examples/client.h +++ b/examples/client.h @@ -164,14 +164,8 @@ public: int on_read(); int on_write(bool retransmit = false); int write_streams(); - int write_0rtt_streams(); int feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data, size_t datalen); - int do_handshake(const ngtcp2_path *path, const uint8_t *data, - size_t datalen); - int do_handshake_read_once(const ngtcp2_path *path, const uint8_t *data, - size_t datalen); - ssize_t do_handshake_write_once(); void schedule_retransmit(); int handshake_completed(); diff --git a/examples/server.cc b/examples/server.cc index 660999279a28f27bdf1850b07d03afe6126deaa8..2fe1960e7ca8f62e2b21015b7bef5e5512f6a696 100644 --- a/examples/server.cc +++ b/examples/server.cc @@ -1993,77 +1993,6 @@ ssize_t Handler::hp_mask(uint8_t *dest, size_t destlen, const uint8_t *key, samplelen); } -int Handler::do_handshake_read_once(const ngtcp2_path *path, - const uint8_t *data, size_t datalen) { - auto rv = ngtcp2_conn_read_handshake(conn_, path, data, datalen, - util::timestamp(loop_)); - if (rv != 0) { - std::cerr << "ngtcp2_conn_read_handshake: " << ngtcp2_strerror(rv) - << std::endl; - if (!last_error_.code) { - last_error_ = quicErrorTransport(rv); - } - return -1; - } - return 0; -} - -ssize_t Handler::do_handshake_write_once() { - auto nwrite = ngtcp2_conn_write_handshake(conn_, sendbuf_.wpos(), max_pktlen_, - util::timestamp(loop_)); - if (nwrite < 0) { - std::cerr << "ngtcp2_conn_write_handshake: " << ngtcp2_strerror(nwrite) - << std::endl; - last_error_ = quicErrorTransport(nwrite); - return -1; - } - - if (nwrite == 0) { - return 0; - } - - sendbuf_.push(nwrite); - - auto rv = server_->send_packet(*endpoint_, remote_addr_, sendbuf_, &wev_); - if (rv == NETWORK_ERR_SEND_BLOCKED) { - schedule_retransmit(); - return rv; - } - if (rv != NETWORK_ERR_OK) { - return rv; - } - ev_timer_again(loop_, &timer_); - - return nwrite; -} - -int Handler::do_handshake(const ngtcp2_path *path, const uint8_t *data, - size_t datalen) { - if (datalen) { - auto rv = do_handshake_read_once(path, data, datalen); - if (rv != 0) { - return rv; - } - } - - if (sendbuf_.size() > 0) { - auto rv = server_->send_packet(*endpoint_, remote_addr_, sendbuf_, &wev_); - if (rv != NETWORK_ERR_OK) { - return rv; - } - } - - for (;;) { - auto nwrite = do_handshake_write_once(); - if (nwrite < 0) { - return nwrite; - } - if (nwrite == 0) { - return 0; - } - } -} - void Handler::update_endpoint(const ngtcp2_addr *addr) { endpoint_ = static_cast<Endpoint *>(addr->user_data); assert(endpoint_); @@ -2084,26 +2013,17 @@ int Handler::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen, const_cast<Endpoint *>(&ep)}, {salen, const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(sa))}}; - if (ngtcp2_conn_get_handshake_completed(conn_)) { - rv = ngtcp2_conn_read_pkt(conn_, &path, data, datalen, - util::timestamp(loop_)); - if (rv != 0) { - std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl; - if (rv == NGTCP2_ERR_DRAINING) { - start_draining_period(); - return NETWORK_ERR_CLOSE_WAIT; - } - if (!last_error_.code) { - last_error_ = quicErrorTransport(rv); - } - return handle_error(); - } - - return 0; - } - - rv = do_handshake(&path, data, datalen); + rv = + ngtcp2_conn_read_pkt(conn_, &path, data, datalen, util::timestamp(loop_)); if (rv != 0) { + std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl; + if (rv == NGTCP2_ERR_DRAINING) { + start_draining_period(); + return NETWORK_ERR_CLOSE_WAIT; + } + if (!last_error_.code) { + last_error_ = quicErrorTransport(rv); + } return handle_error(); } @@ -2146,16 +2066,6 @@ int Handler::on_write() { assert(sendbuf_.left() >= max_pktlen_); - if (!ngtcp2_conn_get_handshake_completed(conn_)) { - rv = do_handshake(nullptr, nullptr, 0); - if (rv == NETWORK_ERR_SEND_BLOCKED) { - schedule_retransmit(); - } - if (rv != NETWORK_ERR_OK) { - return rv; - } - } - rv = write_streams(); if (rv != 0) { if (rv == NETWORK_ERR_SEND_BLOCKED) { @@ -2164,82 +2074,71 @@ int Handler::on_write() { return rv; } - if (!ngtcp2_conn_get_handshake_completed(conn_)) { - schedule_retransmit(); - return 0; - } - - PathStorage path; - - for (;;) { - auto n = ngtcp2_conn_write_pkt(conn_, &path.path, sendbuf_.wpos(), - max_pktlen_, util::timestamp(loop_)); - if (n < 0) { - std::cerr << "ngtcp2_conn_write_pkt: " << ngtcp2_strerror(n) << std::endl; - last_error_ = quicErrorTransport(n); - return handle_error(); - } - if (n == 0) { - break; - } - - sendbuf_.push(n); - - update_endpoint(&path.path.local); - update_remote_addr(&path.path.remote); - - auto rv = server_->send_packet(*endpoint_, remote_addr_, sendbuf_, &wev_); - if (rv == NETWORK_ERR_SEND_BLOCKED) { - schedule_retransmit(); - return rv; - } - if (rv != NETWORK_ERR_OK) { - return rv; - } - reset_idle_timer(); - } - schedule_retransmit(); + return 0; } int Handler::write_streams() { - if (!httpconn_) { - return 0; - } - std::array<nghttp3_vec, 16> vec; int rv; - if (ngtcp2_conn_get_max_data_left(conn_) == 0) { - return 0; - } - for (;;) { int64_t stream_id; int fin; - auto sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin, - vec.data(), vec.size()); - if (sveccnt < 0) { - std::cerr << "nghttp3_conn_writev_stream: " << nghttp3_strerror(sveccnt) - << std::endl; - last_error_ = quicErrorApplication(sveccnt); - return handle_error(); - } + ssize_t sveccnt = 0; - if (sveccnt == 0) { - break; + if (httpconn_ && ngtcp2_conn_get_max_data_left(conn_)) { + sveccnt = nghttp3_conn_writev_stream(httpconn_, &stream_id, &fin, + vec.data(), vec.size()); + if (sveccnt < 0) { + std::cerr << "nghttp3_conn_writev_stream: " << nghttp3_strerror(sveccnt) + << std::endl; + last_error_ = quicErrorApplication(sveccnt); + return handle_error(); + } } ssize_t ndatalen; PathStorage path; + if (sveccnt == 0) { + for (;;) { + auto nwrite = + ngtcp2_conn_write_pkt(conn_, &path.path, sendbuf_.wpos(), + max_pktlen_, util::timestamp(loop_)); + if (nwrite < 0) { + std::cerr << "ngtcp2_conn_write_pkt: " << ngtcp2_strerror(nwrite) + << std::endl; + last_error_ = quicErrorTransport(nwrite); + return handle_error(); + } + if (nwrite == 0) { + return 0; + } + sendbuf_.push(nwrite); + + update_endpoint(&path.path.local); + update_remote_addr(&path.path.remote); + + auto rv = + server_->send_packet(*endpoint_, remote_addr_, sendbuf_, &wev_); + if (rv != NETWORK_ERR_OK) { + return rv; + } + reset_idle_timer(); + + return 0; + } + } + auto v = vec.data(); auto vcnt = static_cast<size_t>(sveccnt); for (;;) { auto nwrite = ngtcp2_conn_writev_stream( - conn_, &path.path, sendbuf_.wpos(), max_pktlen_, &ndatalen, stream_id, - fin, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, + conn_, &path.path, sendbuf_.wpos(), max_pktlen_, &ndatalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, fin, + reinterpret_cast<const ngtcp2_vec *>(v), vcnt, util::timestamp(loop_)); if (nwrite < 0) { auto should_break = false; @@ -2261,6 +2160,17 @@ int Handler::write_streams() { case NGTCP2_ERR_STREAM_SHUT_WR: should_break = true; break; + case NGTCP2_ERR_WRITE_STREAM_MORE: + assert(ndatalen > 0); + rv = nghttp3_conn_add_write_offset(httpconn_, stream_id, ndatalen); + if (rv != 0) { + std::cerr << "nghttp3_conn_add_write_offset: " + << nghttp3_strerror(rv) << std::endl; + last_error_ = quicErrorApplication(rv); + return handle_error(); + } + should_break = true; + break; } if (should_break) { @@ -2312,8 +2222,6 @@ int Handler::write_streams() { } } } - - return 0; } bool Handler::draining() const { return draining_; } diff --git a/examples/server.h b/examples/server.h index c5d60a189123811ca7ff34611a69e7ea6dcffc34..6ad960ad4785e6f811e65b9aa17eb82227310731 100644 --- a/examples/server.h +++ b/examples/server.h @@ -194,11 +194,6 @@ public: int write_streams(); int feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen, uint8_t *data, size_t datalen); - int do_handshake_read_once(const ngtcp2_path *path, const uint8_t *data, - size_t datalen); - ssize_t do_handshake_write_once(); - int do_handshake(const ngtcp2_path *path, const uint8_t *data, - size_t datalen); void schedule_retransmit(); void signal_write(); int handshake_completed(); diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h index 8b6bb4c9995ea2151a9d71cbc1a0f8b281746077..a8200e097dfeaf32a571c4c0ee58cd4b323df64f 100644 --- a/lib/includes/ngtcp2/ngtcp2.h +++ b/lib/includes/ngtcp2/ngtcp2.h @@ -241,6 +241,7 @@ typedef enum { NGTCP2_ERR_CONN_ID_BLOCKED = -237, NGTCP2_ERR_INTERNAL = -238, NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED = -239, + NGTCP2_ERR_WRITE_STREAM_MORE = -240, NGTCP2_ERR_FATAL = -500, NGTCP2_ERR_NOMEM = -501, NGTCP2_ERR_CALLBACK_FAILURE = -502, @@ -1585,110 +1586,13 @@ ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, */ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn); -/** - * @function - * - * `ngtcp2_conn_read_handshake` performs QUIC cryptographic handshake - * by reading given data. |pkt| points to the buffer to read and - * |pktlen| is the length of the buffer. |path| is the network path. - * - * The application should call `ngtcp2_conn_write_handshake` (or - * `ngtcp2_conn_client_write_handshake` for client session) to make - * handshake go forward after calling this function. - * - * Application should call this function until - * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the - * completion of handshake, `ngtcp2_conn_read_pkt` and - * `ngtcp2_conn_write_pkt` should be called instead. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: (TBD). - */ -NGTCP2_EXTERN int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, - const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_handshake` performs QUIC cryptographic handshake - * by writing handshake packets. It may write a packet in the given - * buffer pointed by |dest| whose capacity is given as |destlen|. - * Application must ensure that the buffer pointed by |dest| is not - * empty. - * - * Application should keep calling this function repeatedly until it - * returns zero, or negative error code. - * - * Application should call this function until - * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the - * completion of handshake, `ngtcp2_conn_read_pkt` and - * `ngtcp2_conn_write_pkt` should be called instead. - * - * During handshake, application can send 0-RTT data (or its response) - * using `ngtcp2_conn_write_stream`. - * `ngtcp2_conn_client_write_handshake` is generally efficient because - * it can coalesce Handshake packet and 0-RTT packet into one UDP - * packet. - * - * This function returns 0 if it cannot write any frame because buffer - * is too small, or packet is congestion limited. Application should - * keep reading and wait for congestion window to grow. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns the number of bytes written to the buffer - * pointed by |dest| if it succeeds, or one of the following negative - * error codes: (TBD). - */ -NGTCP2_EXTERN ssize_t ngtcp2_conn_write_handshake(ngtcp2_conn *conn, - uint8_t *dest, size_t destlen, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_client_write_handshake` is just like - * `ngtcp2_conn_write_handshake`, but it is for client only, and can - * write 0-RTT data. This function can coalesce handshake packet and - * 0-RTT packet into single UDP packet, thus it is generally more - * efficient than the combination of `ngtcp2_conn_write_handshake` and - * `ngtcp2_conn_write_stream`. - * - * |stream_id|, |fin|, |datav|, and |datavcnt| are stream identifier - * to which 0-RTT data is sent, whether it is a last data chunk in - * this stream, a vector of 0-RTT data, and its number of elements - * respectively. If there is no 0RTT data to send, pass negative - * integer to |stream_id|. The amount of 0RTT data sent is assigned - * to |*pdatalen|. If no data is sent, -1 is assigned. Note that 0 - * length STREAM frame is allowed in QUIC, so 0 might be assigned to - * |*pdatalen|. - * - * This function returns 0 if it cannot write any frame because buffer - * is too small, or packet is congestion limited. Application should - * keep reading and wait for congestion window to grow. - * - * This function returns the number of bytes written to the buffer - * pointed by |dest| if it succeeds, or one of the following negative - * error codes: (TBD). - */ -NGTCP2_EXTERN ssize_t ngtcp2_conn_client_write_handshake( - ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ssize_t *pdatalen, - int64_t stream_id, uint8_t fin, const ngtcp2_vec *datav, size_t datavcnt, - ngtcp2_tstamp ts); - /** * @function * * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of * length |pktlen| and processes it. |path| is the network path the - * packet is delivered. This function must be called after QUIC - * handshake has finished successfully. + * packet is delivered. This function performs QUIC handshake as + * well. * * This function must not be called from inside the callback * functions. @@ -1709,45 +1613,9 @@ NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, /** * @function * - * `ngtcp2_conn_write_pkt` writes a QUIC packet in the buffer pointed - * by |dest| whose length is |destlen|. |ts| is the timestamp of the - * current time. - * - * If |path| is not NULL, this function stores the network path with - * which the packet should be sent. Each addr field must point to the - * buffer which is at least 128 bytes. ``sizeof(struct - * sockaddr_storage)`` is enough. The assignment might not be done if - * nothing is written to |dest|. - * - * If there is no packet to send, this function returns 0. - * - * Application should keep calling this function repeatedly until it - * returns zero, or negative error code. - * - * This function returns 0 if it cannot write any frame because buffer - * is too small, or packet is congestion limited. Application should - * keep reading and wait for congestion window to grow. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` - * User-defined callback function failed. - * :enum:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * The packet number has reached at the maximum value, therefore - * the function cannot make new packet on this connection. - * - * In general, if the error code which satisfies - * ngtcp2_erro_is_fatal(err) != 0 is returned, the application should - * just close the connection by calling - * `ngtcp2_conn_write_connection_close` or just delete the QUIC - * connection using `ngtcp2_conn_del`. It is undefined to call the - * other library functions. + * `ngtcp2_conn_write_pkt` is equivalent to calling + * `ngtcp2_conn_writev_stream` without specifying stream data and + * :enum:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags. */ NGTCP2_EXTERN ssize_t ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, @@ -2018,9 +1886,8 @@ NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn); * `ngtcp2_conn_loss_detection_expiry` returns the expiry time point * of loss detection timer. Application should call * `ngtcp2_conn_on_loss_detection_timer` and `ngtcp2_conn_write_pkt` - * (or `ngtcp2_conn_write_handshake` if handshake has not finished - * yet) when it expires. It returns UINT64_MAX if loss detection - * timer is not armed. + * (or `ngtcp2_conn_writev_stream`) when it expires. It returns + * UINT64_MAX if loss detection timer is not armed. */ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); @@ -2031,9 +1898,8 @@ ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of * delayed protected ACK. Application should call * ngtcp2_conn_cancel_expired_ack_delay_timer() and - * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_write_handshake` if - * handshake has not finished yet) when it expires. It returns - * UINT64_MAX if there is no expiry. + * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_writev_stream`) when it + * expires. It returns UINT64_MAX if there is no expiry. */ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn); @@ -2225,6 +2091,21 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, uint16_t app_error_code); +/** + * @enum + * + * ngtcp2_write_stream_flag defines extra behaviour for + * `ngtcp2_conn_writev_stream()`. + */ +typedef enum { + NGTCP2_WRITE_STREAM_FLAG_NONE = 0x00, + /** + * NGTCP2_WRITE_STREAM_FLAG_MORE indicates that more stream data may + * come and should be coalesced into the same packet if possible. + */ + NGTCP2_WRITE_STREAM_FLAG_MORE = 0x01 +} ngtcp2_write_stream_flag; + /** * @function * @@ -2234,15 +2115,18 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, */ NGTCP2_EXTERN ssize_t ngtcp2_conn_write_stream( ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, - ssize_t *pdatalen, int64_t stream_id, uint8_t fin, const uint8_t *data, - size_t datalen, ngtcp2_tstamp ts); + ssize_t *pdatalen, uint32_t flags, int64_t stream_id, uint8_t fin, + const uint8_t *data, size_t datalen, ngtcp2_tstamp ts); /** * @function * * `ngtcp2_conn_writev_stream` writes a packet containing stream data * of stream denoted by |stream_id|. The buffer of the packet is - * pointed by |dest| of length |destlen|. + * pointed by |dest| of length |destlen|. This function performs QUIC + * handshake as well. + * + * Specifying -1 to |stream_id| means no new stream data to send. * * If |path| is not NULL, this function stores the network path with * which the packet should be sent. Each addr field must point to the @@ -2250,7 +2134,7 @@ NGTCP2_EXTERN ssize_t ngtcp2_conn_write_stream( * sockaddr_storage)`` is enough. The assignment might not be done if * nothing is written to |dest|. * - * If the all given data is encoded as STREAM frame in|dest|, and if + * If the all given data is encoded as STREAM frame in |dest|, and if * |fin| is nonzero, fin flag is set in outgoing STREAM frame. * Otherwise, fin flag in STREAM frame is not set. * @@ -2264,6 +2148,44 @@ NGTCP2_EXTERN ssize_t ngtcp2_conn_write_stream( * The number of data encoded in STREAM frame is stored in |*pdatalen| * if it is not NULL. * + * If |flags| equals to :enum:`NGTCP2_WRITE_STREAM_FLAG_NONE`, this + * function produces a single payload of UDP packet. If the given + * stream data is small (e.g., few bytes), the packet might be + * severely under filled. Too many small packet might increase + * overall packet processing costs. Unless there are retransmissions, + * by default, application can only send 1 STREAM frame in one QUIC + * packet. In order to include more than 1 STREAM frame in one QUIC + * packet, specify :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|. + * This is analogous to ``MSG_MORE`` flag in ``send(2)``. If the + * :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 + * outcomes: + * + * - The function returns the written length of packet just like + * without :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE`. This is because + * packet is nearly full and the library decided to make a complete + * packet. + * + * - The function returns :enum:`NGTCP2_ERR_WRITE_STREAM_MORE`. This + * indicates that application can call this function with different + * stream data to pack them into the same packet. Application has + * to specify the same |conn|, |path|, |dest|, |destlen|, + * |pdatalen|, and |ts| parameters, otherwise the behaviour is + * undefined. The application can change |flags|. + * + * - The function returns :enum:`NGTCP2_ERR_STREAM_DATA_BLOCKED` which + * indicates that stream is blocked because of flow control. + * + * - The other error might be returned just like without + * :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE`. + * + * When application sees :enum:`NGTCP2_ERR_WRITE_STREAM_MORE`, it must + * not call other ngtcp2 API functions (application can still call + * `ngtcp2_conn_write_connection_close` or + * `ngtcp2_conn_write_application_close` to handle error from this + * function). Just keep calling `ngtcp2_conn_writev_stream` or + * `ngtcp2_conn_write_pkt` until it returns a positive number (which + * indicates a complete packet is ready). + * * This function returns 0 if it cannot write any frame because buffer * is too small, or packet is congestion limited. Application should * keep reading and wait for congestion window to grow. @@ -2290,11 +2212,22 @@ NGTCP2_EXTERN ssize_t ngtcp2_conn_write_stream( * Early data was rejected by server. * :enum:`NGTCP2_ERR_STREAM_DATA_BLOCKED` * Stream is blocked because of flow control. + * :enum:`NGTCP2_ERR_WRITE_STREAM_MORE` + * (Only when :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` is specified) + * Application can call this function to pack more stream data + * into the same packet. See above to know how it works. + * + * In general, if the error code which satisfies + * ngtcp2_err_is_fatal(err) != 0 is returned, the application should + * just close the connection by calling + * `ngtcp2_conn_write_connection_close` or just delete the QUIC + * connection using `ngtcp2_conn_del`. It is undefined to call the + * other library functions. */ NGTCP2_EXTERN ssize_t ngtcp2_conn_writev_stream( ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, - ssize_t *pdatalen, int64_t stream_id, uint8_t fin, const ngtcp2_vec *datav, - size_t datavcnt, ngtcp2_tstamp ts); + ssize_t *pdatalen, uint32_t flags, int64_t stream_id, uint8_t fin, + const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts); /** * @function diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c index 605554ca49792805daa77a89e82c78502b4ca798..23fe906b31e5aa5e465f1ef056758de9e88aaf01 100644 --- a/lib/ngtcp2_conn.c +++ b/lib/ngtcp2_conn.c @@ -28,7 +28,6 @@ #include <assert.h> #include <math.h> -#include "ngtcp2_ppe.h" #include "ngtcp2_macro.h" #include "ngtcp2_log.h" #include "ngtcp2_cid.h" @@ -2096,6 +2095,16 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, return 0; } +typedef enum { + NGTCP2_WRITE_PKT_FLAG_NONE = 0x00, + /* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet + should be padded */ + NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING = 0x01, + /* NGTCP2_WRITE_PKT_FLAG_STREAM_MORE indicates that more stream DATA + may come and it should be encoded into the current packet. */ + NGTCP2_WRITE_PKT_FLAG_STREAM_MORE = 0x02, +} ngtcp2_write_pkt_flag; + /* * conn_write_pkt writes a protected packet in the buffer pointed by * |dest| whose length if |destlen|. |type| specifies the type of @@ -2127,13 +2136,13 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ssize_t *pdatalen, uint8_t type, ngtcp2_strm *data_strm, uint8_t fin, const ngtcp2_vec *datav, size_t datavcnt, - int require_padding, ngtcp2_tstamp ts) { + uint8_t flags, ngtcp2_tstamp ts) { int rv; - ngtcp2_ppe ppe; - ngtcp2_pkt_hd hd; + ngtcp2_crypto_ctx *ctx = &conn->pkt.ctx; + ngtcp2_ppe *ppe = &conn->pkt.ppe; + ngtcp2_pkt_hd *hd = &conn->pkt.hd; ngtcp2_frame *ackfr = NULL, lfr; ssize_t nwrite; - ngtcp2_crypto_ctx ctx; ngtcp2_frame_chain **pfrc, *nfrc, *frc; ngtcp2_stream_frame_chain *nsfrc; ngtcp2_crypto_frame_chain *ncfrc; @@ -2150,34 +2159,9 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, int hd_logged = 0; ngtcp2_path_challenge_entry *pcent; uint8_t hd_flags; - - switch (type) { - case NGTCP2_PKT_SHORT: - hd_flags = - (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) - ? NGTCP2_PKT_FLAG_KEY_PHASE - : NGTCP2_PKT_FLAG_NONE; - ctx.ckm = pktns->crypto.tx.ckm; - ctx.hp = pktns->crypto.tx.hp; - break; - case NGTCP2_PKT_0RTT: - assert(!conn->server); - if (!conn->early.ckm) { - return 0; - } - hd_flags = NGTCP2_PKT_FLAG_LONG_FORM; - ctx.ckm = conn->early.ckm; - ctx.hp = conn->early.hp; - break; - default: - /* Unreachable */ - assert(0); - } - - ctx.aead_overhead = conn->crypto.aead_overhead; - ctx.encrypt = conn->callbacks.encrypt; - ctx.hp_mask = conn->callbacks.hp_mask; - ctx.user_data = conn; + int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; + int stream_more = (flags & NGTCP2_WRITE_PKT_FLAG_STREAM_MORE) != 0; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; if (data_strm) { ndatalen = conn_enforce_flow_control(conn, data_strm, datalen); @@ -2196,221 +2180,225 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, } } - /* TODO Take into account stream frames */ - if ((pktns->tx.frq || send_stream || - ngtcp2_ringbuf_len(&conn->rx.path_challenge) || - conn_should_send_max_data(conn)) && - conn->rx.unsent_max_offset > conn->rx.max_offset) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - return rv; - } - nfrc->fr.type = NGTCP2_FRAME_MAX_DATA; - nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset; - nfrc->next = pktns->tx.frq; - pktns->tx.frq = nfrc; - - conn->rx.max_offset = conn->rx.unsent_max_offset; - } - - ngtcp2_pkt_hd_init(&hd, hd_flags, type, &conn->dcid.current.cid, &conn->oscid, - pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); - - ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); - - rv = ngtcp2_ppe_encode_hd(&ppe, &hd); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return 0; - } - - if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { - return 0; - } - - for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); - - /* PATH_RESPONSE is bound to the path that the corresponding - PATH_CHALLENGE is received. */ - if (!ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { + if (!ppe_pending) { + switch (type) { + case NGTCP2_PKT_SHORT: + hd_flags = + (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE; + ctx->ckm = pktns->crypto.tx.ckm; + ctx->hp = pktns->crypto.tx.hp; break; - } - - lfr.type = NGTCP2_FRAME_PATH_RESPONSE; - memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data)); - - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); + case NGTCP2_PKT_0RTT: + assert(!conn->server); + if (!conn->early.ckm) { + return 0; + } + hd_flags = NGTCP2_PKT_FLAG_LONG_FORM; + ctx->ckm = conn->early.ckm; + ctx->hp = conn->early.hp; break; + default: + /* Unreachable */ + assert(0); } - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); - - pkt_empty = 0; - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; - /* We don't retransmit PATH_RESPONSE. */ - } + ctx->aead_overhead = conn->crypto.aead_overhead; + ctx->encrypt = conn->callbacks.encrypt; + ctx->hp_mask = conn->callbacks.hp_mask; + ctx->user_data = conn; - rv = conn_create_ack_frame(conn, &ackfr, &pktns->acktr, ts, - conn_compute_ack_delay(conn), - conn->local.settings.ack_delay_exponent); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } + /* TODO Take into account stream frames */ + if ((pktns->tx.frq || send_stream || + ngtcp2_ringbuf_len(&conn->rx.path_challenge) || + conn_should_send_max_data(conn)) && + conn->rx.unsent_max_offset > conn->rx.max_offset) { + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_DATA; + nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; - if (ackfr) { - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - ngtcp2_acktr_commit_ack(&pktns->acktr); - ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack); - pkt_empty = 0; + conn->rx.max_offset = conn->rx.unsent_max_offset; } - ngtcp2_mem_free(conn->mem, ackfr); - ackfr = NULL; - } - for (pfrc = &pktns->tx.frq; *pfrc;) { - switch ((*pfrc)->fr.type) { - case NGTCP2_FRAME_STOP_SENDING: - strm = ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id); - if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_STREAM: - assert(0); - break; - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - if ((*pfrc)->fr.max_streams.max_streams < conn->remote.bidi.max_streams) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_MAX_STREAMS_UNI: - if ((*pfrc)->fr.max_streams.max_streams < conn->remote.uni.max_streams) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_MAX_STREAM_DATA: - strm = - ngtcp2_conn_find_stream(conn, (*pfrc)->fr.max_stream_data.stream_id); - if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) || - (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_MAX_DATA: - if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_CRYPTO: - assert(0); - break; - } + ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, + &conn->oscid, pktns->tx.last_pkt_num + 1, + pktns_select_pkt_numlen(pktns), conn->version, 0); - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &(*pfrc)->fr); + ngtcp2_ppe_init(ppe, dest, destlen, ctx); + + rv = ngtcp2_ppe_encode_hd(ppe, hd); if (rv != 0) { assert(NGTCP2_ERR_NOBUF == rv); - break; + return 0; } - pkt_empty = 0; - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; - pfrc = &(*pfrc)->next; - } - - if (rv != NGTCP2_ERR_NOBUF) { - for (; !ngtcp2_pq_empty(&pktns->crypto.tx.frq);) { - left = ngtcp2_ppe_left(&ppe); + if (!ngtcp2_ppe_ensure_hp_sample(ppe)) { + return 0; + } - left = ngtcp2_pkt_crypto_max_datalen( - conn_cryptofrq_top(conn, pktns)->fr.offset, left, left); + for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); - if (left == (size_t)-1) { + /* PATH_RESPONSE is bound to the path that the corresponding + PATH_CHALLENGE is received. */ + if (!ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { break; } - rv = conn_cryptofrq_pop(conn, &ncfrc, pktns, left); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } + lfr.type = NGTCP2_FRAME_PATH_RESPONSE; + memcpy(lfr.path_response.data, pcent->data, + sizeof(lfr.path_response.data)); - if (ncfrc == NULL) { - break; - } - - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, - &ncfrc->frc.fr); + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); if (rv != 0) { - assert(0); + assert(NGTCP2_ERR_NOBUF == rv); + break; } - *pfrc = &ncfrc->frc; - pfrc = &(*pfrc)->next; + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + /* We don't retransmit PATH_RESPONSE. */ } - } - /* Write MAX_STREAM_ID after RESET_STREAM so that we can extend stream - ID space in one packet. */ - if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL && - conn->remote.bidi.unsent_max_streams > conn->remote.bidi.max_streams) { - rv = conn_call_extend_max_remote_streams_bidi( - conn, conn->remote.bidi.unsent_max_streams); + rv = conn_create_ack_frame(conn, &ackfr, &pktns->acktr, ts, + conn_compute_ack_delay(conn), + conn->local.settings.ack_delay_exponent); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; + if (ackfr) { + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, + ackfr->ack.largest_ack); + pkt_empty = 0; + } + ngtcp2_mem_free(conn->mem, ackfr); + ackfr = NULL; } - nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI; - nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams; - *pfrc = nfrc; - conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams; + for (pfrc = &pktns->tx.frq; *pfrc;) { + switch ((*pfrc)->fr.type) { + case NGTCP2_FRAME_STOP_SENDING: + strm = + ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id); + if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_STREAM: + assert(0); + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + if ((*pfrc)->fr.max_streams.max_streams < + conn->remote.bidi.max_streams) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_STREAMS_UNI: + if ((*pfrc)->fr.max_streams.max_streams < + conn->remote.uni.max_streams) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + strm = ngtcp2_conn_find_stream(conn, + (*pfrc)->fr.max_stream_data.stream_id); + if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) || + (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_DATA: + if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_CRYPTO: + assert(0); + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + break; + } - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &(*pfrc)->fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; pfrc = &(*pfrc)->next; } - } - if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL) { - if (conn->remote.uni.unsent_max_streams > conn->remote.uni.max_streams) { - rv = conn_call_extend_max_remote_streams_uni( - conn, conn->remote.uni.unsent_max_streams); + if (rv != NGTCP2_ERR_NOBUF) { + for (; !ngtcp2_pq_empty(&pktns->crypto.tx.frq);) { + left = ngtcp2_ppe_left(ppe); + + left = ngtcp2_pkt_crypto_max_datalen( + conn_cryptofrq_top(conn, pktns)->fr.offset, left, left); + + if (left == (size_t)-1) { + break; + } + + rv = conn_cryptofrq_pop(conn, &ncfrc, pktns, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (ncfrc == NULL) { + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, + &ncfrc->frc.fr); + if (rv != 0) { + assert(0); + } + + *pfrc = &ncfrc->frc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + } + } + + /* Write MAX_STREAM_ID after RESET_STREAM so that we can extend stream + ID space in one packet. */ + if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL && + conn->remote.bidi.unsent_max_streams > conn->remote.bidi.max_streams) { + rv = conn_call_extend_max_remote_streams_bidi( + conn, conn->remote.bidi.unsent_max_streams); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -2421,14 +2409,13 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, assert(ngtcp2_err_is_fatal(rv)); return rv; } - nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI; - nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams; + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI; + nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams; *pfrc = nfrc; - conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams; + conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams; - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, - &(*pfrc)->fr); + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); if (rv != 0) { assert(NGTCP2_ERR_NOBUF == rv); } else { @@ -2437,91 +2424,127 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, pfrc = &(*pfrc)->next; } } - } - if (rv != NGTCP2_ERR_NOBUF) { - for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { - strm = ngtcp2_conn_tx_strmq_top(conn); + if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL) { + if (conn->remote.uni.unsent_max_streams > conn->remote.uni.max_streams) { + rv = conn_call_extend_max_remote_streams_uni( + conn, conn->remote.uni.unsent_max_streams); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } - if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && - strm->rx.max_offset < strm->rx.unsent_max_offset) { rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; } - nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; - nfrc->fr.max_stream_data.stream_id = strm->stream_id; - nfrc->fr.max_stream_data.max_stream_data = strm->rx.unsent_max_offset; - ngtcp2_list_insert(nfrc, pfrc); + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI; + nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams; + *pfrc = nfrc; - rv = - conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr); + conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, + &(*pfrc)->fr); if (rv != 0) { assert(NGTCP2_ERR_NOBUF == rv); - break; + } else { + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pfrc = &(*pfrc)->next; } - - pkt_empty = 0; - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; - pfrc = &(*pfrc)->next; - strm->rx.max_offset = strm->rx.unsent_max_offset; } + } - if (ngtcp2_strm_streamfrq_empty(strm)) { - ngtcp2_conn_tx_strmq_pop(conn); - continue; - } + if (rv != NGTCP2_ERR_NOBUF) { + for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { + strm = ngtcp2_conn_tx_strmq_top(conn); - left = ngtcp2_ppe_left(&ppe); + if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + strm->rx.max_offset < strm->rx.unsent_max_offset) { + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + nfrc->fr.max_stream_data.stream_id = strm->stream_id; + nfrc->fr.max_stream_data.max_stream_data = strm->rx.unsent_max_offset; + ngtcp2_list_insert(nfrc, pfrc); - left = ngtcp2_pkt_stream_max_datalen( - strm->stream_id, ngtcp2_strm_streamfrq_top(strm)->fr.offset, left, - left); + rv = + conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + break; + } - if (left == (size_t)-1) { - break; - } + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pfrc = &(*pfrc)->next; + strm->rx.max_offset = strm->rx.unsent_max_offset; + } - rv = ngtcp2_strm_streamfrq_pop(strm, &nsfrc, left); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } - if (nsfrc == NULL) { - /* TODO Why? */ - break; - } + left = ngtcp2_ppe_left(ppe); - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, - &nsfrc->frc.fr); - if (rv != 0) { - assert(0); - } + left = ngtcp2_pkt_stream_max_datalen( + strm->stream_id, ngtcp2_strm_streamfrq_top(strm)->fr.offset, left, + left); - *pfrc = &nsfrc->frc; - pfrc = &(*pfrc)->next; + if (left == (size_t)-1) { + break; + } - pkt_empty = 0; - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + rv = ngtcp2_strm_streamfrq_pop(strm, &nsfrc, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } - if (ngtcp2_strm_streamfrq_empty(strm)) { - ngtcp2_conn_tx_strmq_pop(conn); - continue; - } + if (nsfrc == NULL) { + /* TODO Why? */ + break; + } - ngtcp2_conn_tx_strmq_pop(conn); - ++strm->cycle; - rv = ngtcp2_conn_tx_strmq_push(conn, strm); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, + &nsfrc->frc.fr); + if (rv != 0) { + assert(0); + } + + *pfrc = &nsfrc->frc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + ngtcp2_conn_tx_strmq_pop(conn); + ++strm->cycle; + rv = ngtcp2_conn_tx_strmq_push(conn, strm); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } } } + } else { + pfrc = conn->pkt.pfrc; + rtb_entry_flags |= conn->pkt.rtb_entry_flags; + pkt_empty = conn->pkt.pkt_empty; } - left = ngtcp2_ppe_left(&ppe); + left = ngtcp2_ppe_left(ppe); if (rv != NGTCP2_ERR_NOBUF && send_stream && *pfrc == NULL && (ndatalen = ngtcp2_pkt_stream_max_datalen(data_strm->stream_id, @@ -2545,8 +2568,7 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, fin = fin && ndatalen == datalen; nsfrc->fr.fin = fin; - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, - &nsfrc->frc.fr); + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nsfrc->frc.fr); if (rv != 0) { assert(0); } @@ -2556,6 +2578,17 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + + data_strm->tx.offset += ndatalen; + conn->tx.offset += ndatalen; + + if (fin) { + ngtcp2_strm_shutdown(data_strm, NGTCP2_STRM_FLAG_SHUT_WR); + } + + if (pdatalen) { + *pdatalen = (ssize_t)ndatalen; + } } else { send_stream = 0; } @@ -2568,30 +2601,44 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, return 0; } + if (stream_more) { + conn->pkt.pfrc = pfrc; + conn->pkt.pkt_empty = pkt_empty; + conn->pkt.rtb_entry_flags = rtb_entry_flags; + conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; + + if (stream_blocked) { + return NGTCP2_ERR_STREAM_DATA_BLOCKED; + } + if (send_stream) { + return NGTCP2_ERR_WRITE_STREAM_MORE; + } + } + /* TODO Push STREAM frame back to ngtcp2_strm if there is an error before ngtcp2_rtb_entry is safely created and added. */ if ((require_padding || (type == NGTCP2_PKT_0RTT && conn->state == NGTCP2_CS_CLIENT_INITIAL)) && - ngtcp2_ppe_left(&ppe)) { + ngtcp2_ppe_left(ppe)) { lfr.type = NGTCP2_FRAME_PADDING; - lfr.padding.len = ngtcp2_ppe_padding(&ppe); - ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + lfr.padding.len = ngtcp2_ppe_padding(ppe); + ngtcp2_log_tx_fr(&conn->log, hd, &lfr); } else { lfr.type = NGTCP2_FRAME_PADDING; - lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(ppe); if (lfr.padding.len) { - ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + ngtcp2_log_tx_fr(&conn->log, hd, &lfr); } } - nwrite = ngtcp2_ppe_final(&ppe, NULL); + nwrite = ngtcp2_ppe_final(ppe, NULL); if (nwrite < 0) { assert(ngtcp2_err_is_fatal((int)nwrite)); return nwrite; } if (*pfrc != pktns->tx.frq) { - rv = ngtcp2_rtb_entry_new(&ent, &hd, NULL, ts, (size_t)nwrite, + rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, rtb_entry_flags, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal((int)nwrite)); @@ -2608,20 +2655,9 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ngtcp2_rtb_entry_del(ent, conn->mem); return rv; } - - if (send_stream) { - data_strm->tx.offset += ndatalen; - conn->tx.offset += ndatalen; - - if (fin) { - ngtcp2_strm_shutdown(data_strm, NGTCP2_STRM_FLAG_SHUT_WR); - } - } } - if (pdatalen && send_stream) { - *pdatalen = (ssize_t)ndatalen; - } + conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING; ++pktns->tx.last_pkt_num; @@ -2981,7 +3017,7 @@ static ssize_t conn_write_probe_pkt(ngtcp2_conn *conn, uint8_t *dest, /* a probe packet is not blocked by cwnd. */ nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, - fin, datav, datavcnt, /* require_padding = */ 0, ts); + fin, datav, datavcnt, NGTCP2_WRITE_PKT_FLAG_NONE, ts); if (nwrite == 0 || nwrite == NGTCP2_ERR_STREAM_DATA_BLOCKED) { nwrite = conn_write_probe_ping(conn, dest, destlen, ts); } @@ -3321,84 +3357,11 @@ static int conn_peer_has_unused_cid(ngtcp2_conn *conn) { ssize_t ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts) { - ssize_t nwrite; - uint64_t cwnd; - ngtcp2_pktns *pktns = &conn->pktns; - size_t origlen = destlen; - int rv; - - conn->log.last_ts = ts; - - if (pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) { - return NGTCP2_ERR_PKT_NUM_EXHAUSTED; - } - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: - case NGTCP2_CS_SERVER_INITIAL: - case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: - return NGTCP2_ERR_INVALID_STATE; - case NGTCP2_CS_POST_HANDSHAKE: - rv = conn_remove_retired_connection_id(conn, ts); - if (rv != 0) { - return rv; - } - - nwrite = conn_write_path_response(conn, path, dest, destlen, ts); - if (nwrite) { - return nwrite; - } - - if (conn->pv && conn_peer_has_unused_cid(conn)) { - nwrite = conn_write_path_challenge(conn, path, dest, destlen, ts); - if (nwrite) { - return nwrite; - } - } - - cwnd = conn_cwnd_left(conn); - destlen = ngtcp2_min(destlen, cwnd); - - if (path) { - ngtcp2_path_copy(path, &conn->dcid.current.ps.path); - } - - if (conn_handshake_remnants_left(conn)) { - nwrite = conn_write_handshake_pkts(conn, dest, destlen, 0, ts); - if (nwrite) { - return nwrite; - } - } - nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); - if (nwrite) { - return nwrite; - } - - if (conn->rcs.probe_pkt_left) { - return conn_write_probe_pkt(conn, dest, origlen, NULL, NULL, 0, NULL, 0, - ts); - } - - nwrite = conn_write_pkt(conn, dest, destlen, NULL, NGTCP2_PKT_SHORT, NULL, - 0, NULL, 0, /* require_padding = */ 0, ts); - if (nwrite < 0) { - assert(nwrite != NGTCP2_ERR_NOBUF); - return nwrite; - } - if (nwrite) { - return nwrite; - } - return conn_write_protected_ack_pkt(conn, dest, origlen, ts); - case NGTCP2_CS_CLOSING: - return NGTCP2_ERR_CLOSING; - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_DRAINING; - default: - return 0; - } + return ngtcp2_conn_writev_stream( + conn, path, dest, destlen, + /* pdatalen = */ NULL, NGTCP2_WRITE_STREAM_FLAG_NONE, + /* stream_id = */ -1, + /* fin = */ 0, /* datav = */ NULL, /* datavcnt = */ 0, ts); } /* @@ -6667,7 +6630,7 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, case NGTCP2_CS_SERVER_INITIAL: case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: - return NGTCP2_ERR_INVALID_STATE; + return ngtcp2_conn_read_handshake(conn, path, pkt, pktlen, ts); case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: @@ -6716,13 +6679,6 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, int rv; ngtcp2_pktns *hs_pktns = &conn->hs_pktns; - conn->log.last_ts = ts; - - if (pktlen > 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu", - pktlen); - } - switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: /* TODO Better to log something when we ignore input */ @@ -6884,7 +6840,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { static ssize_t conn_retransmit_retry_early(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts) { return conn_write_pkt(conn, dest, destlen, NULL, NGTCP2_PKT_0RTT, NULL, 0, - NULL, 0, /* require_padding = */ 0, ts); + NULL, 0, NGTCP2_WRITE_PKT_FLAG_NONE, ts); } /* @@ -6921,12 +6877,6 @@ static ssize_t conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_rcvry_stat *rcs = &conn->rcs; size_t pending_early_datalen; - conn->log.last_ts = ts; - - if (conn_check_pkt_num_exhausted(conn)) { - return NGTCP2_ERR_PKT_NUM_EXHAUSTED; - } - cwnd = conn_cwnd_left(conn); destlen = ngtcp2_min(destlen, cwnd); @@ -7135,24 +7085,21 @@ ssize_t ngtcp2_conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, ssize_t ngtcp2_conn_client_write_handshake(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ssize_t *pdatalen, - int64_t stream_id, uint8_t fin, - const ngtcp2_vec *datav, + uint32_t flags, int64_t stream_id, + uint8_t fin, const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts) { ngtcp2_strm *strm = NULL; int send_stream = 0; ssize_t spktlen, early_spktlen; uint64_t cwnd; - int require_padding; int was_client_initial; size_t datalen = ngtcp2_vec_len(datav, datavcnt); size_t early_datalen = 0; + uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; assert(!conn->server); - if (pdatalen) { - *pdatalen = -1; - } - /* conn->early.ckm might be created in the first call of conn_handshake(). Check it later. */ if (stream_id != -1 && @@ -7180,21 +7127,34 @@ ssize_t ngtcp2_conn_client_write_handshake(ngtcp2_conn *conn, uint8_t *dest, } } - was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL; - spktlen = conn_write_handshake(conn, dest, destlen, early_datalen, ts); + if (!ppe_pending) { + was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL; + spktlen = conn_write_handshake(conn, dest, destlen, early_datalen, ts); - if (spktlen < 0) { - return spktlen; - } + if (spktlen < 0) { + return spktlen; + } - if (conn->pktns.crypto.tx.ckm || !conn->early.ckm || !send_stream) { - return spktlen; + if (conn->pktns.crypto.tx.ckm || !conn->early.ckm || !send_stream) { + return spktlen; + } + } else { + assert(!conn->pktns.crypto.tx.ckm); + assert(conn->early.ckm); + + was_client_initial = conn->pkt.was_client_initial; + spktlen = conn->pkt.hs_spktlen; } /* If spktlen > 0, we are making a compound packet. If Initial packet is written, we have to pad bytes to 0-RTT packet. */ - require_padding = spktlen && was_client_initial; + if (spktlen && was_client_initial) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + if (flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_STREAM_MORE; + } cwnd = conn_cwnd_left(conn); @@ -7202,13 +7162,17 @@ ssize_t ngtcp2_conn_client_write_handshake(ngtcp2_conn *conn, uint8_t *dest, destlen -= (size_t)spktlen; destlen = ngtcp2_min(destlen, cwnd); - early_spktlen = - conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_0RTT, strm, fin, - datav, datavcnt, require_padding, ts); + early_spktlen = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_0RTT, + strm, fin, datav, datavcnt, wflags, ts); if (early_spktlen < 0) { - if (early_spktlen == NGTCP2_ERR_STREAM_DATA_BLOCKED) { + switch (early_spktlen) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: return spktlen; + case NGTCP2_ERR_WRITE_STREAM_MORE: + conn->pkt.was_client_initial = was_client_initial; + conn->pkt.hs_spktlen = spktlen; + break; } return early_spktlen; } @@ -7750,24 +7714,26 @@ ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) { ssize_t ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, - ssize_t *pdatalen, int64_t stream_id, - uint8_t fin, const uint8_t *data, - size_t datalen, ngtcp2_tstamp ts) { + ssize_t *pdatalen, uint32_t flags, + int64_t stream_id, uint8_t fin, + const uint8_t *data, size_t datalen, + ngtcp2_tstamp ts) { ngtcp2_vec datav; datav.len = datalen; datav.base = (uint8_t *)data; - return ngtcp2_conn_writev_stream(conn, path, dest, destlen, pdatalen, + return ngtcp2_conn_writev_stream(conn, path, dest, destlen, pdatalen, flags, stream_id, fin, &datav, 1, ts); } ssize_t ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, - ssize_t *pdatalen, int64_t stream_id, - uint8_t fin, const ngtcp2_vec *datav, - size_t datavcnt, ngtcp2_tstamp ts) { - ngtcp2_strm *strm; + ssize_t *pdatalen, uint32_t flags, + int64_t stream_id, uint8_t fin, + const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { + ngtcp2_strm *strm = NULL; ssize_t nwrite; uint64_t cwnd; ngtcp2_pktns *pktns = &conn->pktns; @@ -7775,6 +7741,8 @@ ssize_t ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, size_t server_hs_tx_left; ngtcp2_rcvry_stat *rcs = &conn->rcs; int rv; + uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; conn->log.last_ts = ts; @@ -7783,10 +7751,37 @@ ssize_t ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, } switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + return ngtcp2_conn_client_write_handshake(conn, dest, destlen, pdatalen, + flags, stream_id, fin, datav, + datavcnt, ts); + case NGTCP2_CS_SERVER_INITIAL: + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + nwrite = ngtcp2_conn_write_handshake(conn, dest, destlen, ts); + if (nwrite) { + return nwrite; + } + if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { + return 0; + } + break; + case NGTCP2_CS_POST_HANDSHAKE: + break; case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: return NGTCP2_ERR_DRAINING; + default: + return 0; } if (conn_check_pkt_num_exhausted(conn)) { @@ -7798,25 +7793,29 @@ ssize_t ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, return rv; } - strm = ngtcp2_conn_find_stream(conn, stream_id); - if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; - } - - if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { - return NGTCP2_ERR_STREAM_SHUT_WR; - } + if (stream_id != -1) { + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } - nwrite = conn_write_path_response(conn, path, dest, destlen, ts); - if (nwrite) { - return nwrite; + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return NGTCP2_ERR_STREAM_SHUT_WR; + } } - if (conn->pv && conn_peer_has_unused_cid(conn)) { - nwrite = conn_write_path_challenge(conn, path, dest, destlen, ts); + if (!ppe_pending) { + nwrite = conn_write_path_response(conn, path, dest, destlen, ts); if (nwrite) { return nwrite; } + + if (conn->pv && conn_peer_has_unused_cid(conn)) { + nwrite = conn_write_path_challenge(conn, path, dest, destlen, ts); + if (nwrite) { + return nwrite; + } + } } cwnd = conn_cwnd_left(conn); @@ -7825,6 +7824,7 @@ ssize_t ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, if (conn->server) { server_hs_tx_left = conn_server_hs_tx_left(conn); if (server_hs_tx_left == 0) { + assert(!ppe_pending); if (rcs->loss_detection_timer) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "loss detection timer canceled"); @@ -7839,47 +7839,45 @@ ssize_t ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } - if (conn_handshake_remnants_left(conn)) { - nwrite = conn_write_handshake_pkts(conn, dest, destlen, 0, ts); + if (!ppe_pending) { + if (conn_handshake_remnants_left(conn)) { + nwrite = conn_write_handshake_pkts(conn, dest, destlen, 0, ts); + if (nwrite) { + return nwrite; + } + } + nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); if (nwrite) { return nwrite; } } - nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); - if (nwrite) { - return nwrite; + + if (flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_STREAM_MORE; } - if (pktns->crypto.tx.ckm) { - if (conn->rcs.probe_pkt_left) { - return conn_write_probe_pkt(conn, dest, origlen, pdatalen, strm, fin, - datav, datavcnt, ts); - } + assert(pktns->crypto.tx.ckm); - nwrite = - conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, - fin, datav, datavcnt, /* require_padding = */ 0, ts); - if (nwrite < 0) { - assert(nwrite != NGTCP2_ERR_NOBUF); - return nwrite; - } - if (nwrite == 0) { - return conn_write_protected_ack_pkt(conn, dest, origlen, ts); - } - return nwrite; + if (ppe_pending) { + return conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, + fin, datav, datavcnt, wflags, ts); } - /* Send STREAM frame in 0-RTT packet. */ - if (conn->server || !conn->early.ckm) { - return NGTCP2_ERR_NOKEY; + if (conn->rcs.probe_pkt_left) { + return conn_write_probe_pkt(conn, dest, origlen, pdatalen, strm, fin, datav, + datavcnt, ts); } - if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { - return NGTCP2_ERR_EARLY_DATA_REJECTED; + nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, + fin, datav, datavcnt, wflags, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; } - - return conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_0RTT, strm, - fin, datav, datavcnt, /* require_padding = */ 0, ts); + if (nwrite == 0) { + return conn_write_protected_ack_pkt(conn, dest, origlen, ts); + } + return nwrite; } ssize_t ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_path *path, diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h index ac3195c407e877cb3a609afbccd861fdab394df7..f03134ae49a8128f788b3503793714e032643dfd 100644 --- a/lib/ngtcp2_conn.h +++ b/lib/ngtcp2_conn.h @@ -46,6 +46,7 @@ #include "ngtcp2_pv.h" #include "ngtcp2_cid.h" #include "ngtcp2_buf.h" +#include "ngtcp2_ppe.h" typedef enum { /* Client specific handshake states */ @@ -183,6 +184,10 @@ typedef enum { endpoint has initiated key update and waits for the remote endpoint to update key. */ NGTCP2_CONN_FLAG_WAIT_FOR_REMOTE_KEY_UPDATE = 0x0800, + /* NGTCP2_CONN_FLAG_PPE_PENDING is set when + NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state + of ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */ + NGTCP2_CONN_FLAG_PPE_PENDING = 0x1000, } ngtcp2_conn_flag; typedef struct { @@ -425,6 +430,19 @@ struct ngtcp2_conn { ngtcp2_array decrypt_buf; } crypto; + /* pkt contains the packet intermediate construction data to support + NGTCP2_WRITE_STREAM_FLAG_MORE */ + struct { + ngtcp2_crypto_ctx ctx; + ngtcp2_pkt_hd hd; + ngtcp2_ppe ppe; + ngtcp2_frame_chain **pfrc; + int pkt_empty; + uint8_t rtb_entry_flags; + int was_client_initial; + ssize_t hs_spktlen; + } pkt; + ngtcp2_map strms; ngtcp2_rcvry_stat rcs; ngtcp2_cc_stat ccs; @@ -449,6 +467,102 @@ struct ngtcp2_conn { int server; }; +/** + * @function + * + * `ngtcp2_conn_read_handshake` performs QUIC cryptographic handshake + * by reading given data. |pkt| points to the buffer to read and + * |pktlen| is the length of the buffer. |path| is the network path. + * + * The application should call `ngtcp2_conn_write_handshake` (or + * `ngtcp2_conn_client_write_handshake` for client session) to make + * handshake go forward after calling this function. + * + * Application should call this function until + * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the + * completion of handshake, `ngtcp2_conn_read_pkt` and + * `ngtcp2_conn_write_pkt` should be called instead. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: (TBD). + */ +int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_handshake` performs QUIC cryptographic handshake + * by writing handshake packets. It may write a packet in the given + * buffer pointed by |dest| whose capacity is given as |destlen|. + * Application must ensure that the buffer pointed by |dest| is not + * empty. + * + * Application should keep calling this function repeatedly until it + * returns zero, or negative error code. + * + * Application should call this function until + * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the + * completion of handshake, `ngtcp2_conn_read_pkt` and + * `ngtcp2_conn_write_pkt` should be called instead. + * + * During handshake, application can send 0-RTT data (or its response) + * using `ngtcp2_conn_write_stream`. + * `ngtcp2_conn_client_write_handshake` is generally efficient because + * it can coalesce Handshake packet and 0-RTT packet into one UDP + * packet. + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited. Application should + * keep reading and wait for congestion window to grow. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest| if it succeeds, or one of the following negative + * error codes: (TBD). + */ +ssize_t ngtcp2_conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_client_write_handshake` is just like + * `ngtcp2_conn_write_handshake`, but it is for client only, and can + * write 0-RTT data. This function can coalesce handshake packet and + * 0-RTT packet into single UDP packet, thus it is generally more + * efficient than the combination of `ngtcp2_conn_write_handshake` and + * `ngtcp2_conn_write_stream`. + * + * |stream_id|, |fin|, |datav|, and |datavcnt| are stream identifier + * to which 0-RTT data is sent, whether it is a last data chunk in + * this stream, a vector of 0-RTT data, and its number of elements + * respectively. If there is no 0RTT data to send, pass negative + * integer to |stream_id|. The amount of 0RTT data sent is assigned + * to |*pdatalen|. If no data is sent, -1 is assigned. Note that 0 + * length STREAM frame is allowed in QUIC, so 0 might be assigned to + * |*pdatalen|. + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited. Application should + * keep reading and wait for congestion window to grow. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest| if it succeeds, or one of the following negative + * error codes: (TBD). + */ +ssize_t ngtcp2_conn_client_write_handshake(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ssize_t *pdatalen, + uint32_t flags, int64_t stream_id, + uint8_t fin, const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts); + /* * ngtcp2_conn_sched_ack stores packet number |pkt_num| and its * reception timestamp |ts| in order to send its ACK. diff --git a/lib/ngtcp2_err.c b/lib/ngtcp2_err.c index 082e51f44d5fae7a5cecd7eb42423eb08bda7ea6..3d324ea24fa3ca2fb905b912fd1effed53691968 100644 --- a/lib/ngtcp2_err.c +++ b/lib/ngtcp2_err.c @@ -96,6 +96,8 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_INTERNAL"; case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: return "ERR_CRYPTO_BUFFER_EXCEEDED"; + case NGTCP2_ERR_WRITE_STREAM_MORE: + return "ERR_WRITE_STREAM_MORE"; default: return "(unknown)"; } diff --git a/tests/main.c b/tests/main.c index d35f4a8510248312a357687f8c86234ee6c8e199..4e25746a05454b75d215a98b6873efd334a1c762 100644 --- a/tests/main.c +++ b/tests/main.c @@ -198,8 +198,6 @@ int main() { !CU_add_test(pSuite, "conn_handshake", test_ngtcp2_conn_handshake) || !CU_add_test(pSuite, "conn_handshake_error", test_ngtcp2_conn_handshake_error) || - !CU_add_test(pSuite, "conn_client_write_handshake", - test_ngtcp2_conn_client_write_handshake) || !CU_add_test(pSuite, "conn_retransmit_protected", test_ngtcp2_conn_retransmit_protected) || !CU_add_test(pSuite, "conn_send_max_stream_data", @@ -226,7 +224,7 @@ int main() { !CU_add_test(pSuite, "conn_server_path_validation", test_ngtcp2_conn_server_path_validation) || !CU_add_test(pSuite, "conn_client_connection_migration", - test_ngtcp2_conn_client_write_handshake) || + test_ngtcp2_conn_client_connection_migration) || !CU_add_test(pSuite, "conn_recv_path_challenge", test_ngtcp2_conn_recv_path_challenge) || !CU_add_test(pSuite, "conn_key_update", test_ngtcp2_conn_key_update) || diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c index ea08b1bd1a9122293614228bf449fc493a221625..d3c644fa2d82a2156fd8c45d3015e6974bb5612e 100644 --- a/tests/ngtcp2_conn_test.c +++ b/tests/ngtcp2_conn_test.c @@ -643,8 +643,9 @@ void test_ngtcp2_conn_stream_open_close(void) { CU_ASSERT(fr.stream.offset == strm->rx.last_offset); CU_ASSERT(fr.stream.offset == ngtcp2_strm_rx_offset(strm)); - spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, 4, 1, - NULL, 0, 3); + spktlen = + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, 1, NULL, 0, 3); CU_ASSERT(spktlen > 0); @@ -807,27 +808,31 @@ void test_ngtcp2_conn_stream_tx_flow_control(void) { strm = ngtcp2_conn_find_stream(conn, stream_id); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 1); CU_ASSERT(spktlen > 0); CU_ASSERT(1024 == nwrite); CU_ASSERT(1024 == strm->tx.offset); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 2); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 2); CU_ASSERT(spktlen > 0); CU_ASSERT(1023 == nwrite); CU_ASSERT(2047 == strm->tx.offset); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 3); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 3); CU_ASSERT(NGTCP2_ERR_STREAM_DATA_BLOCKED == spktlen); /* We can write 0 length STREAM frame */ spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 0, 3); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 0, 3); CU_ASSERT(spktlen > 0); CU_ASSERT(0 == nwrite); @@ -845,7 +850,8 @@ void test_ngtcp2_conn_stream_tx_flow_control(void) { CU_ASSERT(2048 == strm->tx.max_offset); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 5); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 5); CU_ASSERT(spktlen > 0); CU_ASSERT(1 == nwrite); @@ -863,7 +869,8 @@ void test_ngtcp2_conn_stream_tx_flow_control(void) { CU_ASSERT(0 == rv); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 1, null_data, 1024, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 1, null_data, 1024, 1); CU_ASSERT(0 == spktlen); CU_ASSERT(-1 == nwrite); @@ -981,28 +988,32 @@ void test_ngtcp2_conn_tx_flow_control(void) { CU_ASSERT(0 == rv); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 1); CU_ASSERT(spktlen > 0); CU_ASSERT(1024 == nwrite); CU_ASSERT(1024 == conn->tx.offset); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1023, 2); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1023, 2); CU_ASSERT(spktlen > 0); CU_ASSERT(1023 == nwrite); CU_ASSERT(1024 + 1023 == conn->tx.offset); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 3); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 3); CU_ASSERT(spktlen > 0); CU_ASSERT(1 == nwrite); CU_ASSERT(2048 == conn->tx.offset); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 4); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 4); CU_ASSERT(NGTCP2_ERR_STREAM_DATA_BLOCKED == spktlen); CU_ASSERT(-1 == nwrite); @@ -1018,7 +1029,8 @@ void test_ngtcp2_conn_tx_flow_control(void) { CU_ASSERT(3072 == conn->tx.max_offset); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite, - stream_id, 0, null_data, 1024, 4); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 1024, 4); CU_ASSERT(spktlen > 0); CU_ASSERT(1024 == nwrite); @@ -1051,7 +1063,8 @@ void test_ngtcp2_conn_shutdown_stream_write(void) { setup_default_client(&conn); ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); - ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, stream_id, 0, + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, 0, null_data, 1239, 1); rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01); @@ -1190,8 +1203,9 @@ void test_ngtcp2_conn_recv_reset_stream(void) { CU_ASSERT(0 == rv); - ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, 4, 0, null_data, - 354, 2); + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, 0, null_data, 354, + 2); fr.type = NGTCP2_FRAME_RESET_STREAM; fr.reset_stream.stream_id = 4; @@ -1227,8 +1241,9 @@ void test_ngtcp2_conn_recv_reset_stream(void) { CU_ASSERT(0 == rv); - ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, 4, 0, null_data, - 354, 2); + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, 0, null_data, 354, + 2); ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01); ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 3); @@ -1262,8 +1277,9 @@ void test_ngtcp2_conn_recv_reset_stream(void) { CU_ASSERT(0 == rv); - ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, 4, 0, null_data, - 354, 2); + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, 0, null_data, 354, + 2); ngtcp2_conn_shutdown_stream_write(conn, 4, NGTCP2_APP_ERR01); ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 3); @@ -1309,8 +1325,9 @@ void test_ngtcp2_conn_recv_reset_stream(void) { CU_ASSERT(0 == rv); - ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, 4, 0, null_data, - 354, 2); + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, 0, null_data, 354, + 2); fr.type = NGTCP2_FRAME_STOP_SENDING; fr.stop_sending.stream_id = 4; @@ -1615,7 +1632,8 @@ void test_ngtcp2_conn_recv_stop_sending(void) { setup_default_client(&conn); ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); - ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, stream_id, 0, + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, 0, null_data, 333, ++t); fr.type = NGTCP2_FRAME_STOP_SENDING; @@ -1649,7 +1667,8 @@ void test_ngtcp2_conn_recv_stop_sending(void) { setup_default_client(&conn); ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); - ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, stream_id, 0, + ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, 0, null_data, 333, ++t); fr.type = NGTCP2_FRAME_RESET_STREAM; @@ -1859,7 +1878,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, @@ -1875,7 +1895,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, @@ -1891,7 +1912,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, @@ -1907,7 +1929,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, @@ -1923,7 +1946,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, @@ -1939,7 +1963,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT( @@ -1955,7 +1980,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT( @@ -1971,7 +1997,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT( @@ -1987,7 +2014,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT( @@ -2003,7 +2031,8 @@ void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 19, 1); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); CU_ASSERT( @@ -2150,7 +2179,7 @@ void test_ngtcp2_conn_recv_retry(void) { setup_handshake_client(&conn); conn->callbacks.recv_retry = recv_retry; - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -2164,12 +2193,11 @@ void test_ngtcp2_conn_recv_retry(void) { CU_ASSERT(spktlen > 0); - rv = - ngtcp2_conn_read_handshake(conn, &null_path, buf, (size_t)spktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, (size_t)spktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); if (i == 1) { /* Retry packet was ignored */ @@ -2188,7 +2216,7 @@ void test_ngtcp2_conn_recv_retry(void) { setup_handshake_client(&conn); conn->callbacks.recv_retry = recv_retry; - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -2200,11 +2228,11 @@ void test_ngtcp2_conn_recv_retry(void) { CU_ASSERT(spktlen > 0); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, (size_t)spktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, (size_t)spktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(0 == spktlen); @@ -2218,16 +2246,16 @@ void test_ngtcp2_conn_recv_retry(void) { CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_client_write_handshake(conn, buf, sizeof(buf), &datalen, - stream_id, 0, - null_datav(&datav, 219), 1, ++t); + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_datav(&datav, 219), 1, ++t); CU_ASSERT(sizeof(buf) == spktlen); CU_ASSERT(219 == datalen); - spktlen = - ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen, - stream_id, 0, null_datav(&datav, 119), 1, ++t); + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_datav(&datav, 119), 1, ++t); CU_ASSERT(spktlen > 0); CU_ASSERT(119 == datalen); @@ -2240,11 +2268,11 @@ void test_ngtcp2_conn_recv_retry(void) { CU_ASSERT(spktlen > 0); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, (size_t)spktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, (size_t)spktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 219 + 119); CU_ASSERT(2 == conn->pktns.tx.last_pkt_num); @@ -2255,7 +2283,8 @@ void test_ngtcp2_conn_recv_retry(void) { /* ngtcp2_conn_write_stream sends new 0RTT packet. */ spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &datalen, - stream_id, 0, null_data, 120, ++t); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 120, ++t); CU_ASSERT(spktlen > 0); CU_ASSERT(3 == conn->pktns.tx.last_pkt_num); @@ -2394,11 +2423,11 @@ void test_ngtcp2_conn_handshake(void) { conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -2421,7 +2450,7 @@ void test_ngtcp2_conn_handshake_error(void) { /* client side */ setup_handshake_client(&conn); conn->callbacks.recv_crypto_data = recv_crypto_handshake_error; - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -2435,7 +2464,7 @@ void test_ngtcp2_conn_handshake_error(void) { conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(NGTCP2_ERR_CRYPTO == rv); @@ -2455,95 +2484,13 @@ void test_ngtcp2_conn_handshake_error(void) { conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(NGTCP2_ERR_CRYPTO == rv); ngtcp2_conn_del(conn); } -void test_ngtcp2_conn_client_write_handshake(void) { - ngtcp2_conn *conn; - uint8_t buf[1240]; - ssize_t spktlen; - ngtcp2_tstamp t = 0; - int64_t stream_id; - int rv; - ssize_t datalen; - ngtcp2_vec datav; - - /* Verify that Handshake packet and 0-RTT packet are coalesced into - one UDP packet. */ - setup_early_client(&conn); - - rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); - - CU_ASSERT(0 == rv); - - spktlen = ngtcp2_conn_client_write_handshake(conn, buf, sizeof(buf), &datalen, - stream_id, 0, - null_datav(&datav, 199), 1, ++t); - - CU_ASSERT(sizeof(buf) == spktlen); - CU_ASSERT(199 == datalen); - - ngtcp2_conn_del(conn); - - /* 0 length 0-RTT packet with FIN bit set */ - setup_early_client(&conn); - - rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); - - CU_ASSERT(0 == rv); - - spktlen = ngtcp2_conn_client_write_handshake(conn, buf, sizeof(buf), &datalen, - stream_id, 1, NULL, 0, ++t); - - CU_ASSERT(sizeof(buf) == spktlen); - CU_ASSERT(0 == datalen); - - ngtcp2_conn_del(conn); - - /* Can write 0 length STREAM frame */ - setup_early_client(&conn); - - rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); - - CU_ASSERT(0 == rv); - - spktlen = ngtcp2_conn_client_write_handshake(conn, buf, sizeof(buf), &datalen, - -1, 0, NULL, 0, ++t); - - CU_ASSERT(spktlen > 0); - - /* We have written Initial. Now check that STREAM frame is - written. */ - spktlen = ngtcp2_conn_client_write_handshake(conn, buf, sizeof(buf), &datalen, - stream_id, 0, NULL, 0, ++t); - - CU_ASSERT(spktlen > 0); - - ngtcp2_conn_del(conn); - - /* Could not send 0-RTT data because buffer is too small. */ - setup_early_client(&conn); - - rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); - - CU_ASSERT(0 == rv); - - spktlen = ngtcp2_conn_client_write_handshake( - conn, buf, - NGTCP2_MIN_LONG_HEADERLEN + 1 + ngtcp2_conn_get_dcid(conn)->datalen + - conn->oscid.datalen + 300, - &datalen, stream_id, 1, NULL, 0, ++t); - - CU_ASSERT(spktlen > 0); - CU_ASSERT(-1 == datalen); - - ngtcp2_conn_del(conn); -} - void test_ngtcp2_conn_retransmit_protected(void) { ngtcp2_conn *conn; uint8_t buf[2048]; @@ -2557,7 +2504,8 @@ void test_ngtcp2_conn_retransmit_protected(void) { ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL, - stream_id, 0, null_data, 126, ++t); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 126, ++t); CU_ASSERT(spktlen > 0); @@ -3359,6 +3307,7 @@ void test_ngtcp2_conn_send_early_data(void) { int64_t stream_id; int rv; ngtcp2_tstamp t = 0; + ngtcp2_vec datav; setup_early_client(&conn); @@ -3366,15 +3315,12 @@ void test_ngtcp2_conn_send_early_data(void) { CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); - - CU_ASSERT(spktlen > 0); - spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &datalen, - stream_id, 1, null_data, 1024, ++t); + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 1, null_data, 1024, ++t); CU_ASSERT((ssize_t)sizeof(buf) == spktlen); - CU_ASSERT(700 == datalen); + CU_ASSERT(417 == datalen); ngtcp2_conn_del(conn); @@ -3386,34 +3332,102 @@ void test_ngtcp2_conn_send_early_data(void) { CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, 606, &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_data, 10, ++t); CU_ASSERT(spktlen > 0); + CU_ASSERT(-1 == datalen); + + ngtcp2_conn_del(conn); + + /* +1 buffer size */ + setup_early_client(&conn); - spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, 323, &datalen, stream_id, + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, 607, &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, 0, null_data, 10, ++t); CU_ASSERT(spktlen > 0); - CU_ASSERT(-1 == datalen); + CU_ASSERT(1 == datalen); ngtcp2_conn_del(conn); - /* +1 buffer size */ + /* Verify that Handshake packet and 0-RTT packet are coalesced into + one UDP packet. */ + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, null_datav(&datav, 199), 1, ++t); + + CU_ASSERT(sizeof(buf) == spktlen); + CU_ASSERT(199 == datalen); + + ngtcp2_conn_del(conn); + + /* 0 length 0-RTT packet with FIN bit set */ + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 1, NULL, 0, ++t); + + CU_ASSERT(sizeof(buf) == spktlen); + CU_ASSERT(0 == datalen); + + ngtcp2_conn_del(conn); + + /* Can write 0 length STREAM frame */ setup_early_client(&conn); rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, -1, 0, + NULL, 0, ++t); CU_ASSERT(spktlen > 0); - spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, 324, &datalen, stream_id, - 0, null_data, 10, ++t); + /* We have written Initial. Now check that STREAM frame is + written. */ + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + 0, NULL, 0, ++t); CU_ASSERT(spktlen > 0); - CU_ASSERT(1 == datalen); + + ngtcp2_conn_del(conn); + + /* Could not send 0-RTT data because buffer is too small. */ + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream( + conn, NULL, buf, + NGTCP2_MIN_LONG_HEADERLEN + 1 + ngtcp2_conn_get_dcid(conn)->datalen + + conn->oscid.datalen + 300, + &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, 1, NULL, 0, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(-1 == datalen); ngtcp2_conn_del(conn); } @@ -3444,11 +3458,11 @@ void test_ngtcp2_conn_recv_early_data(void) { conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -3464,11 +3478,11 @@ void test_ngtcp2_conn_recv_early_data(void) { conn, buf, sizeof(buf), NGTCP2_PKT_0RTT, &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -3494,11 +3508,11 @@ void test_ngtcp2_conn_recv_early_data(void) { conn, buf, sizeof(buf), NGTCP2_PKT_0RTT, &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(0 == spktlen); @@ -3512,11 +3526,11 @@ void test_ngtcp2_conn_recv_early_data(void) { conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -3552,11 +3566,11 @@ void test_ngtcp2_conn_recv_early_data(void) { conn, buf + pktlen, sizeof(buf) - pktlen, NGTCP2_PKT_0RTT, &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -3597,11 +3611,11 @@ void test_ngtcp2_conn_recv_compound_pkt(void) { conn, buf + pktlen, sizeof(buf) - pktlen, NGTCP2_PKT_INITIAL, &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr); - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen > 0); @@ -3687,12 +3701,12 @@ void test_ngtcp2_conn_pkt_payloadlen(void) { write_pkt_payloadlen(buf, dcid, &conn->oscid, payloadlen + 1); /* The incoming packet should be ignored */ - rv = ngtcp2_conn_read_handshake(conn, &null_path, buf, pktlen, ++t); + rv = ngtcp2_conn_read_pkt(conn, &null_path, buf, pktlen, ++t); CU_ASSERT(0 == rv); CU_ASSERT(NGTCP2_CS_SERVER_INITIAL == conn->state); - spktlen = ngtcp2_conn_write_handshake(conn, buf, sizeof(buf), ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); CU_ASSERT(spktlen == 0); CU_ASSERT(0 == ngtcp2_ksl_len(&conn->in_pktns.acktr.ents)); @@ -3709,6 +3723,7 @@ void test_ngtcp2_conn_writev_stream(void) { int64_t stream_id; ngtcp2_vec datav = {null_data, 10}; ssize_t datalen; + size_t left; /* 0 length STREAM should not be written if we supply nonzero length data. */ @@ -3728,7 +3743,8 @@ void test_ngtcp2_conn_writev_stream(void) { * STREAM overhead (+3) * AEAD overhead (16) */ - spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 39, &datalen, stream_id, + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 39, &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, 0, &datav, 1, ++t); CU_ASSERT(0 == spktlen); @@ -3748,13 +3764,85 @@ void test_ngtcp2_conn_writev_stream(void) { CU_ASSERT(0 == rv); - spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 40, &datalen, stream_id, + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 40, &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, 0, &datav, 1, ++t); CU_ASSERT(spktlen > 0); CU_ASSERT(1 == datalen); ngtcp2_conn_del(conn); + + /* Coalesces multiple STREAM frames */ + setup_default_client(&conn); + conn->local.bidi.max_streams = 100; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + 0, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_STREAM_MORE == spktlen); + CU_ASSERT(10 == datalen); + + left = ngtcp2_ppe_left(&conn->pkt.ppe); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + 0, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_STREAM_MORE == spktlen); + CU_ASSERT(10 == datalen); + CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + ngtcp2_conn_del(conn); + + /* 0RTT: Coalesces multiple STREAM frames */ + setup_early_client(&conn); + conn->local.bidi.max_streams = 100; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + 0, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_STREAM_MORE == spktlen); + CU_ASSERT(10 == datalen); + + left = ngtcp2_ppe_left(&conn->pkt.ppe); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + 0, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_STREAM_MORE == spktlen); + CU_ASSERT(10 == datalen); + CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t); + + /* Make sure that packet is padded */ + CU_ASSERT(1200 == spktlen); + + ngtcp2_conn_del(conn); } void test_ngtcp2_conn_recv_new_connection_id(void) { @@ -3906,7 +3994,6 @@ void test_ngtcp2_conn_client_connection_migration(void) { ngtcp2_conn *conn; uint8_t buf[2048]; size_t pktlen; - ssize_t spktlen; ngtcp2_tstamp t = 900; int64_t pkt_num = 0; ngtcp2_frame fr; @@ -3934,22 +4021,7 @@ void test_ngtcp2_conn_client_connection_migration(void) { rv = ngtcp2_conn_initiate_migration(conn, &new_path, ++t); CU_ASSERT(0 == rv); - CU_ASSERT(NULL != conn->pv); - - spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), t); - - CU_ASSERT(spktlen > 0); - CU_ASSERT(ngtcp2_ringbuf_len(&conn->pv->ents) > 0); - - fr.type = NGTCP2_FRAME_PATH_RESPONSE; - memset(fr.path_response.data, 0, sizeof(fr.path_response.data)); - - pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, - ++pkt_num, &fr); - - rv = ngtcp2_conn_read_pkt(conn, &new_path, buf, pktlen, ++t); - - CU_ASSERT(0 == rv); + CU_ASSERT(NULL == conn->pv); CU_ASSERT(ngtcp2_path_eq(&new_path, &conn->dcid.current.ps.path)); CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->dcid.current.cid)); diff --git a/tests/ngtcp2_conn_test.h b/tests/ngtcp2_conn_test.h index 6ddd4b2ac8013ad27d0c1c47c7a28d64384939fa..5cbf9aad49c5f2e66136ae2a6b8074f226f512ac 100644 --- a/tests/ngtcp2_conn_test.h +++ b/tests/ngtcp2_conn_test.h @@ -47,7 +47,6 @@ void test_ngtcp2_conn_recv_delayed_handshake_pkt(void); void test_ngtcp2_conn_recv_max_streams(void); void test_ngtcp2_conn_handshake(void); void test_ngtcp2_conn_handshake_error(void); -void test_ngtcp2_conn_client_write_handshake(void); void test_ngtcp2_conn_retransmit_protected(void); void test_ngtcp2_conn_send_max_stream_data(void); void test_ngtcp2_conn_recv_stream_data(void);