summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:30:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:30:55 +0000
commit17e81f2cd1843f01838245eae7b5ed5edf83d6be (patch)
treea0f685dff11ce5a2dc546a7b46a48bae5d1c0140 /tests
parentInitial commit. (diff)
downloadngtcp2-17e81f2cd1843f01838245eae7b5ed5edf83d6be.tar.xz
ngtcp2-17e81f2cd1843f01838245eae7b5ed5edf83d6be.zip
Adding upstream version 0.12.1+dfsg.upstream/0.12.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/CMakeLists.txt68
-rw-r--r--tests/Makefile.am94
-rw-r--r--tests/main.c357
-rw-r--r--tests/ngtcp2_acktr_test.c367
-rw-r--r--tests/ngtcp2_acktr_test.h37
-rw-r--r--tests/ngtcp2_conn_test.c9351
-rw-r--r--tests/ngtcp2_conn_test.h103
-rw-r--r--tests/ngtcp2_conv_test.c430
-rw-r--r--tests/ngtcp2_conv_test.h46
-rw-r--r--tests/ngtcp2_crypto_test.c665
-rw-r--r--tests/ngtcp2_crypto_test.h35
-rw-r--r--tests/ngtcp2_gaptr_test.c127
-rw-r--r--tests/ngtcp2_gaptr_test.h36
-rw-r--r--tests/ngtcp2_idtr_test.c79
-rw-r--r--tests/ngtcp2_idtr_test.h34
-rw-r--r--tests/ngtcp2_ksl_test.c502
-rw-r--r--tests/ngtcp2_ksl_test.h39
-rw-r--r--tests/ngtcp2_map_test.c206
-rw-r--r--tests/ngtcp2_map_test.h38
-rw-r--r--tests/ngtcp2_pkt_test.c1644
-rw-r--r--tests/ngtcp2_pkt_test.h68
-rw-r--r--tests/ngtcp2_pmtud_test.c153
-rw-r--r--tests/ngtcp2_pmtud_test.h34
-rw-r--r--tests/ngtcp2_pv_test.c129
-rw-r--r--tests/ngtcp2_pv_test.h35
-rw-r--r--tests/ngtcp2_range_test.c105
-rw-r--r--tests/ngtcp2_range_test.h36
-rw-r--r--tests/ngtcp2_ringbuf_test.c91
-rw-r--r--tests/ngtcp2_ringbuf_test.h35
-rw-r--r--tests/ngtcp2_rob_test.c552
-rw-r--r--tests/ngtcp2_rob_test.h37
-rw-r--r--tests/ngtcp2_rtb_test.c470
-rw-r--r--tests/ngtcp2_rtb_test.h38
-rw-r--r--tests/ngtcp2_str_test.c96
-rw-r--r--tests/ngtcp2_str_test.h36
-rw-r--r--tests/ngtcp2_strm_test.c575
-rw-r--r--tests/ngtcp2_strm_test.h36
-rw-r--r--tests/ngtcp2_test_helper.c404
-rw-r--r--tests/ngtcp2_test_helper.h191
-rw-r--r--tests/ngtcp2_vec_test.c426
-rw-r--r--tests/ngtcp2_vec_test.h36
42 files changed, 17842 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..ba2906d
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1 @@
+main
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..db09b7b
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,68 @@
+# ngtcp2
+
+# Copyright (c) 2016 ngtcp2 contributors
+# Copyright (c) 2012 nghttp2 contributors
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(HAVE_CUNIT)
+ include_directories(
+ "${CMAKE_SOURCE_DIR}/lib"
+ "${CMAKE_SOURCE_DIR}/lib/includes"
+ "${CMAKE_BINARY_DIR}/lib/includes"
+ ${CUNIT_INCLUDE_DIRS}
+ )
+
+ set(main_SOURCES
+ main.c
+ ngtcp2_pkt_test.c
+ ngtcp2_range_test.c
+ ngtcp2_rob_test.c
+ ngtcp2_acktr_test.c
+ ngtcp2_map_test.c
+ ngtcp2_crypto_test.c
+ ngtcp2_rtb_test.c
+ ngtcp2_idtr_test.c
+ ngtcp2_conn_test.c
+ ngtcp2_ringbuf_test.c
+ ngtcp2_conv_test.c
+ ngtcp2_test_helper.c
+ ngtcp2_ksl_test.c
+ ngtcp2_gaptr_test.c
+ ngtcp2_vec_test.c
+ ngtcp2_strm_test.c
+ ngtcp2_pv_test.c
+ ngtcp2_pmtud_test.c
+ ngtcp2_str_test.c
+ )
+
+ add_executable(main EXCLUDE_FROM_ALL
+ ${main_SOURCES}
+ )
+ target_include_directories(main PRIVATE ${CUNIT_INCLUDE_DIRS})
+ # FIXME enable and fix warnings
+ #set_target_properties(main PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}")
+ target_link_libraries(main
+ ngtcp2_static
+ ${CUNIT_LIBRARIES}
+ )
+ add_test(main main)
+ add_dependencies(check main)
+endif()
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..3e21c3b
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,94 @@
+# ngtcp2
+
+# Copyright (c) 2016 ngtcp2 contributors
+# Copyright (c) 2012 nghttp2 contributors
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+EXTRA_DIST = CMakeLists.txt
+
+if HAVE_CUNIT
+
+check_PROGRAMS = main
+
+OBJECTS = \
+ main.c \
+ ngtcp2_pkt_test.c \
+ ngtcp2_range_test.c \
+ ngtcp2_rob_test.c \
+ ngtcp2_acktr_test.c \
+ ngtcp2_map_test.c \
+ ngtcp2_crypto_test.c \
+ ngtcp2_rtb_test.c \
+ ngtcp2_idtr_test.c \
+ ngtcp2_conn_test.c \
+ ngtcp2_ringbuf_test.c \
+ ngtcp2_conv_test.c \
+ ngtcp2_ksl_test.c \
+ ngtcp2_gaptr_test.c \
+ ngtcp2_vec_test.c \
+ ngtcp2_strm_test.c \
+ ngtcp2_pv_test.c \
+ ngtcp2_pmtud_test.c \
+ ngtcp2_str_test.c \
+ ngtcp2_test_helper.c
+HFILES= \
+ ngtcp2_pkt_test.h \
+ ngtcp2_range_test.h \
+ ngtcp2_rob_test.h \
+ ngtcp2_acktr_test.h \
+ ngtcp2_map_test.h \
+ ngtcp2_crypto_test.h \
+ ngtcp2_rtb_test.h \
+ ngtcp2_idtr_test.h \
+ ngtcp2_conn_test.h \
+ ngtcp2_ringbuf_test.h \
+ ngtcp2_conv_test.h \
+ ngtcp2_ksl_test.h \
+ ngtcp2_gaptr_test.h \
+ ngtcp2_vec_test.h \
+ ngtcp2_strm_test.h \
+ ngtcp2_pv_test.h \
+ ngtcp2_pmtud_test.h \
+ ngtcp2_str_test.h \
+ ngtcp2_test_helper.h
+
+main_SOURCES = $(HFILES) $(OBJECTS)
+
+# With static lib disabled and symbol hiding enabled, we have to link object
+# files directly because the tests use symbols not included in public API.
+if ENABLE_SHARED
+main_LDADD = ${top_builddir}/lib/.libs/*.o
+else
+main_LDADD = ${top_builddir}/lib/.libs/libngtcp2.la
+endif
+main_LDADD += @CUNIT_LIBS@
+main_LDFLAGS = -static
+
+AM_CFLAGS = $(WARNCFLAGS) \
+ -I${top_srcdir}/lib \
+ -I${top_srcdir}/lib/includes \
+ -I${top_builddir}/lib/includes \
+ -DBUILDING_NGTCP2 \
+ @CUNIT_CFLAGS@ @DEFS@
+AM_LDFLAGS = -no-install
+
+TESTS = main
+
+endif # HAVE_CUNIT
diff --git a/tests/main.c b/tests/main.c
new file mode 100644
index 0000000..d6cd17c
--- /dev/null
+++ b/tests/main.c
@@ -0,0 +1,357 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2016 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <string.h>
+#include <CUnit/Basic.h>
+/* include test cases' include files here */
+#include "ngtcp2_pkt_test.h"
+#include "ngtcp2_range_test.h"
+#include "ngtcp2_rob_test.h"
+#include "ngtcp2_rtb_test.h"
+#include "ngtcp2_acktr_test.h"
+#include "ngtcp2_crypto_test.h"
+#include "ngtcp2_idtr_test.h"
+#include "ngtcp2_conn_test.h"
+#include "ngtcp2_ringbuf_test.h"
+#include "ngtcp2_conv_test.h"
+#include "ngtcp2_ksl_test.h"
+#include "ngtcp2_map_test.h"
+#include "ngtcp2_gaptr_test.h"
+#include "ngtcp2_vec_test.h"
+#include "ngtcp2_strm_test.h"
+#include "ngtcp2_pv_test.h"
+#include "ngtcp2_pmtud_test.h"
+#include "ngtcp2_str_test.h"
+
+static int init_suite1(void) { return 0; }
+
+static int clean_suite1(void) { return 0; }
+
+int main(void) {
+ CU_pSuite pSuite = NULL;
+ unsigned int num_tests_failed;
+
+ /* initialize the CUnit test registry */
+ if (CUE_SUCCESS != CU_initialize_registry())
+ return (int)CU_get_error();
+
+ /* add a suite to the registry */
+ pSuite = CU_add_suite("libngtcp2_TestSuite", init_suite1, clean_suite1);
+ if (NULL == pSuite) {
+ CU_cleanup_registry();
+ return (int)CU_get_error();
+ }
+
+ init_static_path();
+
+ /* add the tests to the suite */
+ if (!CU_add_test(pSuite, "pkt_decode_version_cid",
+ test_ngtcp2_pkt_decode_version_cid) ||
+ !CU_add_test(pSuite, "pkt_decode_hd_long",
+ test_ngtcp2_pkt_decode_hd_long) ||
+ !CU_add_test(pSuite, "pkt_decode_hd_short",
+ test_ngtcp2_pkt_decode_hd_short) ||
+ !CU_add_test(pSuite, "pkt_decode_stream_frame",
+ test_ngtcp2_pkt_decode_stream_frame) ||
+ !CU_add_test(pSuite, "pkt_decode_ack_frame",
+ test_ngtcp2_pkt_decode_ack_frame) ||
+ !CU_add_test(pSuite, "pkt_decode_padding_frame",
+ test_ngtcp2_pkt_decode_padding_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_stream_frame",
+ test_ngtcp2_pkt_encode_stream_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_ack_frame",
+ test_ngtcp2_pkt_encode_ack_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_ack_ecn_frame",
+ test_ngtcp2_pkt_encode_ack_ecn_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_reset_stream_frame",
+ test_ngtcp2_pkt_encode_reset_stream_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_connection_close_frame",
+ test_ngtcp2_pkt_encode_connection_close_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_connection_close_app_frame",
+ test_ngtcp2_pkt_encode_connection_close_app_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_max_data_frame",
+ test_ngtcp2_pkt_encode_max_data_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_max_stream_data_frame",
+ test_ngtcp2_pkt_encode_max_stream_data_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_max_streams_frame",
+ test_ngtcp2_pkt_encode_max_streams_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_ping_frame",
+ test_ngtcp2_pkt_encode_ping_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_data_blocked_frame",
+ test_ngtcp2_pkt_encode_data_blocked_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_stream_data_blocked_frame",
+ test_ngtcp2_pkt_encode_stream_data_blocked_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_streams_blocked_frame",
+ test_ngtcp2_pkt_encode_streams_blocked_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_new_connection_id_frame",
+ test_ngtcp2_pkt_encode_new_connection_id_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_stop_sending_frame",
+ test_ngtcp2_pkt_encode_stop_sending_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_path_challenge_frame",
+ test_ngtcp2_pkt_encode_path_challenge_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_path_response_frame",
+ test_ngtcp2_pkt_encode_path_response_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_crypto_frame",
+ test_ngtcp2_pkt_encode_crypto_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_new_token_frame",
+ test_ngtcp2_pkt_encode_new_token_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_retire_connection_id",
+ test_ngtcp2_pkt_encode_retire_connection_id_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_handshake_done",
+ test_ngtcp2_pkt_encode_handshake_done_frame) ||
+ !CU_add_test(pSuite, "pkt_encode_datagram_frame",
+ test_ngtcp2_pkt_encode_datagram_frame) ||
+ !CU_add_test(pSuite, "pkt_adjust_pkt_num",
+ test_ngtcp2_pkt_adjust_pkt_num) ||
+ !CU_add_test(pSuite, "pkt_validate_ack", test_ngtcp2_pkt_validate_ack) ||
+ !CU_add_test(pSuite, "pkt_write_stateless_reset",
+ test_ngtcp2_pkt_write_stateless_reset) ||
+ !CU_add_test(pSuite, "pkt_write_retry", test_ngtcp2_pkt_write_retry) ||
+ !CU_add_test(pSuite, "pkt_write_version_negotiation",
+ test_ngtcp2_pkt_write_version_negotiation) ||
+ !CU_add_test(pSuite, "pkt_stream_max_datalen",
+ test_ngtcp2_pkt_stream_max_datalen) ||
+ !CU_add_test(pSuite, "get_varint", test_ngtcp2_get_varint) ||
+ !CU_add_test(pSuite, "get_uvarintlen", test_ngtcp2_get_uvarintlen) ||
+ !CU_add_test(pSuite, "put_uvarintlen", test_ngtcp2_put_uvarintlen) ||
+ !CU_add_test(pSuite, "get_uint64", test_ngtcp2_get_uint64) ||
+ !CU_add_test(pSuite, "get_uint48", test_ngtcp2_get_uint48) ||
+ !CU_add_test(pSuite, "get_uint32", test_ngtcp2_get_uint32) ||
+ !CU_add_test(pSuite, "get_uint24", test_ngtcp2_get_uint24) ||
+ !CU_add_test(pSuite, "get_uint16", test_ngtcp2_get_uint16) ||
+ !CU_add_test(pSuite, "get_uint16be", test_ngtcp2_get_uint16be) ||
+ !CU_add_test(pSuite, "nth_server_bidi_id",
+ test_ngtcp2_nth_server_bidi_id) ||
+ !CU_add_test(pSuite, "nth_server_uni_id",
+ test_ngtcp2_nth_server_uni_id) ||
+ !CU_add_test(pSuite, "nth_client_bidi_id",
+ test_ngtcp2_nth_client_bidi_id) ||
+ !CU_add_test(pSuite, "nth_client_uni_id",
+ test_ngtcp2_nth_client_uni_id) ||
+ !CU_add_test(pSuite, "range_intersect", test_ngtcp2_range_intersect) ||
+ !CU_add_test(pSuite, "range_cut", test_ngtcp2_range_cut) ||
+ !CU_add_test(pSuite, "range_not_after", test_ngtcp2_range_not_after) ||
+ !CU_add_test(pSuite, "ksl_insert", test_ngtcp2_ksl_insert) ||
+ !CU_add_test(pSuite, "ksl_clear", test_ngtcp2_ksl_clear) ||
+ !CU_add_test(pSuite, "ksl_range", test_ngtcp2_ksl_range) ||
+ !CU_add_test(pSuite, "ksl_update_key_range",
+ test_ngtcp2_ksl_update_key_range) ||
+ !CU_add_test(pSuite, "ksl_dup", test_ngtcp2_ksl_dup) ||
+ !CU_add_test(pSuite, "ksl_remove_hint", test_ngtcp2_ksl_remove_hint) ||
+ !CU_add_test(pSuite, "rob_push", test_ngtcp2_rob_push) ||
+ !CU_add_test(pSuite, "rob_push_random", test_ngtcp2_rob_push_random) ||
+ !CU_add_test(pSuite, "rob_data_at", test_ngtcp2_rob_data_at) ||
+ !CU_add_test(pSuite, "rob_remove_prefix",
+ test_ngtcp2_rob_remove_prefix) ||
+ !CU_add_test(pSuite, "acktr_add", test_ngtcp2_acktr_add) ||
+ !CU_add_test(pSuite, "acktr_eviction", test_ngtcp2_acktr_eviction) ||
+ !CU_add_test(pSuite, "acktr_forget", test_ngtcp2_acktr_forget) ||
+ !CU_add_test(pSuite, "acktr_recv_ack", test_ngtcp2_acktr_recv_ack) ||
+ !CU_add_test(pSuite, "encode_transport_params",
+ test_ngtcp2_encode_transport_params) ||
+ !CU_add_test(pSuite, "decode_transport_params_new",
+ test_ngtcp2_decode_transport_params_new) ||
+ !CU_add_test(pSuite, "rtb_add", test_ngtcp2_rtb_add) ||
+ !CU_add_test(pSuite, "rtb_recv_ack", test_ngtcp2_rtb_recv_ack) ||
+ !CU_add_test(pSuite, "rtb_lost_pkt_ts", test_ngtcp2_rtb_lost_pkt_ts) ||
+ !CU_add_test(pSuite, "rtb_remove_expired_lost_pkt",
+ test_ngtcp2_rtb_remove_expired_lost_pkt) ||
+ !CU_add_test(pSuite, "rtb_remove_excessive_lost_pkt",
+ test_ngtcp2_rtb_remove_excessive_lost_pkt) ||
+ !CU_add_test(pSuite, "idtr_open", test_ngtcp2_idtr_open) ||
+ !CU_add_test(pSuite, "ringbuf_push_front",
+ test_ngtcp2_ringbuf_push_front) ||
+ !CU_add_test(pSuite, "ringbuf_pop_front",
+ test_ngtcp2_ringbuf_pop_front) ||
+ !CU_add_test(pSuite, "conn_stream_open_close",
+ test_ngtcp2_conn_stream_open_close) ||
+ !CU_add_test(pSuite, "conn_stream_rx_flow_control",
+ test_ngtcp2_conn_stream_rx_flow_control) ||
+ !CU_add_test(pSuite, "conn_stream_rx_flow_control_error",
+ test_ngtcp2_conn_stream_rx_flow_control_error) ||
+ !CU_add_test(pSuite, "conn_stream_tx_flow_control",
+ test_ngtcp2_conn_stream_tx_flow_control) ||
+ !CU_add_test(pSuite, "conn_rx_flow_control",
+ test_ngtcp2_conn_rx_flow_control) ||
+ !CU_add_test(pSuite, "conn_rx_flow_control_error",
+ test_ngtcp2_conn_rx_flow_control_error) ||
+ !CU_add_test(pSuite, "conn_tx_flow_control",
+ test_ngtcp2_conn_tx_flow_control) ||
+ !CU_add_test(pSuite, "conn_shutdown_stream_write",
+ test_ngtcp2_conn_shutdown_stream_write) ||
+ !CU_add_test(pSuite, "conn_recv_reset_stream",
+ test_ngtcp2_conn_recv_reset_stream) ||
+ !CU_add_test(pSuite, "conn_recv_stop_sending",
+ test_ngtcp2_conn_recv_stop_sending) ||
+ !CU_add_test(pSuite, "conn_recv_conn_id_omitted",
+ test_ngtcp2_conn_recv_conn_id_omitted) ||
+ !CU_add_test(pSuite, "conn_short_pkt_type",
+ test_ngtcp2_conn_short_pkt_type) ||
+ !CU_add_test(pSuite, "conn_recv_stateless_reset",
+ test_ngtcp2_conn_recv_stateless_reset) ||
+ !CU_add_test(pSuite, "conn_recv_retry", test_ngtcp2_conn_recv_retry) ||
+ !CU_add_test(pSuite, "conn_recv_delayed_handshake_pkt",
+ test_ngtcp2_conn_recv_delayed_handshake_pkt) ||
+ !CU_add_test(pSuite, "conn_recv_max_streams",
+ test_ngtcp2_conn_recv_max_streams) ||
+ !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_retransmit_protected",
+ test_ngtcp2_conn_retransmit_protected) ||
+ !CU_add_test(pSuite, "conn_send_max_stream_data",
+ test_ngtcp2_conn_send_max_stream_data) ||
+ !CU_add_test(pSuite, "conn_recv_stream_data",
+ test_ngtcp2_conn_recv_stream_data) ||
+ !CU_add_test(pSuite, "conn_recv_ping", test_ngtcp2_conn_recv_ping) ||
+ !CU_add_test(pSuite, "conn_recv_max_stream_data",
+ test_ngtcp2_conn_recv_max_stream_data) ||
+ !CU_add_test(pSuite, "conn_send_early_data",
+ test_ngtcp2_conn_send_early_data) ||
+ !CU_add_test(pSuite, "conn_recv_early_data",
+ test_ngtcp2_conn_recv_early_data) ||
+ !CU_add_test(pSuite, "conn_recv_compound_pkt",
+ test_ngtcp2_conn_recv_compound_pkt) ||
+ !CU_add_test(pSuite, "conn_pkt_payloadlen",
+ test_ngtcp2_conn_pkt_payloadlen) ||
+ !CU_add_test(pSuite, "conn_writev_stream",
+ test_ngtcp2_conn_writev_stream) ||
+ !CU_add_test(pSuite, "conn_writev_datagram",
+ test_ngtcp2_conn_writev_datagram) ||
+ !CU_add_test(pSuite, "conn_recv_datagram",
+ test_ngtcp2_conn_recv_datagram) ||
+ !CU_add_test(pSuite, "conn_recv_new_connection_id",
+ test_ngtcp2_conn_recv_new_connection_id) ||
+ !CU_add_test(pSuite, "conn_recv_retire_connection_id",
+ test_ngtcp2_conn_recv_retire_connection_id) ||
+ !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_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) ||
+ !CU_add_test(pSuite, "conn_crypto_buffer_exceeded",
+ test_ngtcp2_conn_crypto_buffer_exceeded) ||
+ !CU_add_test(pSuite, "conn_handshake_probe",
+ test_ngtcp2_conn_handshake_probe) ||
+ !CU_add_test(pSuite, "conn_handshake_loss",
+ test_ngtcp2_conn_handshake_loss) ||
+ !CU_add_test(pSuite, "conn_recv_client_initial_retry",
+ test_ngtcp2_conn_recv_client_initial_retry) ||
+ !CU_add_test(pSuite, "conn_recv_client_initial_token",
+ test_ngtcp2_conn_recv_client_initial_token) ||
+ !CU_add_test(pSuite, "conn_get_active_dcid",
+ test_ngtcp2_conn_get_active_dcid) ||
+ !CU_add_test(pSuite, "conn_recv_version_negotiation",
+ test_ngtcp2_conn_recv_version_negotiation) ||
+ !CU_add_test(pSuite, "conn_send_initial_token",
+ test_ngtcp2_conn_send_initial_token) ||
+ !CU_add_test(pSuite, "conn_set_remote_transport_params",
+ test_ngtcp2_conn_set_remote_transport_params) ||
+ !CU_add_test(pSuite, "conn_write_connection_close",
+ test_ngtcp2_conn_write_connection_close) ||
+ !CU_add_test(pSuite, "conn_write_application_close",
+ test_ngtcp2_conn_write_application_close) ||
+ !CU_add_test(pSuite, "conn_rtb_reclaim_on_pto",
+ test_ngtcp2_conn_rtb_reclaim_on_pto) ||
+ !CU_add_test(pSuite, "conn_rtb_reclaim_on_pto_datagram",
+ test_ngtcp2_conn_rtb_reclaim_on_pto_datagram) ||
+ !CU_add_test(pSuite, "conn_validate_ecn",
+ test_ngtcp2_conn_validate_ecn) ||
+ !CU_add_test(pSuite, "conn_path_validation",
+ test_ngtcp2_conn_path_validation) ||
+ !CU_add_test(pSuite, "conn_early_data_sync_stream_data_limit",
+ test_ngtcp2_conn_early_data_sync_stream_data_limit) ||
+ !CU_add_test(pSuite, "conn_early_data_rejected",
+ test_ngtcp2_conn_early_data_rejected) ||
+ !CU_add_test(pSuite, "conn_keep_alive", test_ngtcp2_conn_keep_alive) ||
+ !CU_add_test(pSuite, "conn_retire_stale_bound_dcid",
+ test_ngtcp2_conn_retire_stale_bound_dcid) ||
+ !CU_add_test(pSuite, "conn_get_scid", test_ngtcp2_conn_get_scid) ||
+ !CU_add_test(pSuite, "conn_stream_close",
+ test_ngtcp2_conn_stream_close) ||
+ !CU_add_test(pSuite, "conn_buffer_pkt", test_ngtcp2_conn_buffer_pkt) ||
+ !CU_add_test(pSuite, "conn_handshake_timeout",
+ test_ngtcp2_conn_handshake_timeout) ||
+ !CU_add_test(pSuite, "conn_get_connection_close_error",
+ test_ngtcp2_conn_get_connection_close_error) ||
+ !CU_add_test(pSuite, "conn_version_negotiation",
+ test_ngtcp2_conn_version_negotiation) ||
+ !CU_add_test(pSuite, "conn_server_negotiate_version",
+ test_ngtcp2_conn_server_negotiate_version) ||
+ !CU_add_test(pSuite, "conn_pmtud_loss", test_ngtcp2_conn_pmtud_loss) ||
+ !CU_add_test(pSuite, "conn_amplification",
+ test_ngtcp2_conn_amplification) ||
+ !CU_add_test(pSuite, "conn_new_failmalloc",
+ test_ngtcp2_conn_new_failmalloc) ||
+ !CU_add_test(pSuite, "accept", test_ngtcp2_accept) ||
+ !CU_add_test(pSuite, "select_version", test_ngtcp2_select_version) ||
+ !CU_add_test(pSuite, "pkt_write_connection_close",
+ test_ngtcp2_pkt_write_connection_close) ||
+ !CU_add_test(pSuite, "map", test_ngtcp2_map) ||
+ !CU_add_test(pSuite, "map_functional", test_ngtcp2_map_functional) ||
+ !CU_add_test(pSuite, "map_each_free", test_ngtcp2_map_each_free) ||
+ !CU_add_test(pSuite, "map_clear", test_ngtcp2_map_clear) ||
+ !CU_add_test(pSuite, "gaptr_push", test_ngtcp2_gaptr_push) ||
+ !CU_add_test(pSuite, "gaptr_is_pushed", test_ngtcp2_gaptr_is_pushed) ||
+ !CU_add_test(pSuite, "gaptr_drop_first_gap",
+ test_ngtcp2_gaptr_drop_first_gap) ||
+ !CU_add_test(pSuite, "vec_split", test_ngtcp2_vec_split) ||
+ !CU_add_test(pSuite, "vec_merge", test_ngtcp2_vec_merge) ||
+ !CU_add_test(pSuite, "vec_len_varint", test_ngtcp2_vec_len_varint) ||
+ !CU_add_test(pSuite, "strm_streamfrq_pop",
+ test_ngtcp2_strm_streamfrq_pop) ||
+ !CU_add_test(pSuite, "strm_streamfrq_unacked_offset",
+ test_ngtcp2_strm_streamfrq_unacked_offset) ||
+ !CU_add_test(pSuite, "strm_streamfrq_unacked_pop",
+ test_ngtcp2_strm_streamfrq_unacked_pop) ||
+ !CU_add_test(pSuite, "pv_add_entry", test_ngtcp2_pv_add_entry) ||
+ !CU_add_test(pSuite, "pv_validate", test_ngtcp2_pv_validate) ||
+ !CU_add_test(pSuite, "pmtud_probe", test_ngtcp2_pmtud_probe) ||
+ !CU_add_test(pSuite, "encode_ipv4", test_ngtcp2_encode_ipv4) ||
+ !CU_add_test(pSuite, "encode_ipv6", test_ngtcp2_encode_ipv6) ||
+ !CU_add_test(pSuite, "get_bytes", test_ngtcp2_get_bytes)) {
+ CU_cleanup_registry();
+ return (int)CU_get_error();
+ }
+
+ /* Run all tests using the CUnit Basic interface */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ num_tests_failed = CU_get_number_of_tests_failed();
+ CU_cleanup_registry();
+ if (CU_get_error() == CUE_SUCCESS) {
+ return (int)num_tests_failed;
+ } else {
+ printf("CUnit Error: %s\n", CU_get_error_msg());
+ return (int)CU_get_error();
+ }
+}
diff --git a/tests/ngtcp2_acktr_test.c b/tests/ngtcp2_acktr_test.c
new file mode 100644
index 0000000..f75336a
--- /dev/null
+++ b/tests/ngtcp2_acktr_test.c
@@ -0,0 +1,367 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_acktr_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_acktr.h"
+#include "ngtcp2_test_helper.h"
+
+void test_ngtcp2_acktr_add(void) {
+ const int64_t pkt_nums[] = {1, 5, 7, 6, 2, 3};
+ ngtcp2_acktr acktr;
+ ngtcp2_acktr_entry *ent;
+ ngtcp2_ksl_it it;
+ size_t i;
+ int rv;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_log log;
+
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ for (i = 0; i < ngtcp2_arraylen(pkt_nums); ++i) {
+ rv = ngtcp2_acktr_add(&acktr, pkt_nums[i], 1, 999);
+
+ CU_ASSERT(0 == rv);
+ }
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(7 == ent->pkt_num);
+ CU_ASSERT(3 == ent->len);
+
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(3 == ent->pkt_num);
+ CU_ASSERT(3 == ent->len);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_acktr_free(&acktr);
+
+ /* Check all conditions */
+
+ /* The lower bound returns the one beyond of the last entry. The
+ added packet number extends the first entry. */
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ ngtcp2_acktr_add(&acktr, 1, 1, 100);
+ ngtcp2_acktr_add(&acktr, 0, 1, 101);
+
+ CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(1 == ent->pkt_num);
+ CU_ASSERT(2 == ent->len);
+ CU_ASSERT(100 == ent->tstamp);
+
+ ngtcp2_acktr_free(&acktr);
+
+ /* The entry is the first one and adding a packet number extends it
+ to the forward. */
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ ngtcp2_acktr_add(&acktr, 0, 1, 100);
+ ngtcp2_acktr_add(&acktr, 1, 1, 101);
+
+ CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(1 == ent->pkt_num);
+ CU_ASSERT(2 == ent->len);
+ CU_ASSERT(101 == ent->tstamp);
+
+ ngtcp2_acktr_free(&acktr);
+
+ /* The adding entry merges the existing 2 entries. */
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ ngtcp2_acktr_add(&acktr, 0, 1, 100);
+ ngtcp2_acktr_add(&acktr, 2, 1, 101);
+ ngtcp2_acktr_add(&acktr, 3, 1, 102);
+
+ CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents));
+
+ ngtcp2_acktr_add(&acktr, 1, 1, 103);
+
+ CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(3 == ent->pkt_num);
+ CU_ASSERT(4 == ent->len);
+ CU_ASSERT(102 == ent->tstamp);
+
+ ngtcp2_acktr_free(&acktr);
+
+ /* Adding entry does not merge the existing 2 entries. It extends
+ the last entry. */
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ ngtcp2_acktr_add(&acktr, 0, 1, 100);
+ ngtcp2_acktr_add(&acktr, 3, 1, 101);
+ ngtcp2_acktr_add(&acktr, 4, 1, 102);
+
+ CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents));
+
+ ngtcp2_acktr_add(&acktr, 1, 1, 103);
+
+ CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(4 == ent->pkt_num);
+ CU_ASSERT(2 == ent->len);
+ CU_ASSERT(102 == ent->tstamp);
+
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(1 == ent->pkt_num);
+ CU_ASSERT(2 == ent->len);
+ CU_ASSERT(103 == ent->tstamp);
+
+ ngtcp2_acktr_free(&acktr);
+
+ /* Adding entry does not merge the existing 2 entries. It extends
+ the first entry. */
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ ngtcp2_acktr_add(&acktr, 0, 1, 100);
+ ngtcp2_acktr_add(&acktr, 3, 1, 101);
+ ngtcp2_acktr_add(&acktr, 4, 1, 102);
+
+ CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents));
+
+ ngtcp2_acktr_add(&acktr, 2, 1, 103);
+
+ CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(4 == ent->pkt_num);
+ CU_ASSERT(3 == ent->len);
+ CU_ASSERT(102 == ent->tstamp);
+
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(0 == ent->pkt_num);
+ CU_ASSERT(1 == ent->len);
+ CU_ASSERT(100 == ent->tstamp);
+
+ ngtcp2_acktr_free(&acktr);
+
+ /* The added packet number does not extend any entries. */
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ ngtcp2_acktr_add(&acktr, 0, 1, 0);
+ ngtcp2_acktr_add(&acktr, 4, 1, 0);
+ ngtcp2_acktr_add(&acktr, 2, 1, 0);
+
+ CU_ASSERT(3 == ngtcp2_ksl_len(&acktr.ents));
+
+ ngtcp2_acktr_free(&acktr);
+}
+
+void test_ngtcp2_acktr_eviction(void) {
+ ngtcp2_acktr acktr;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+ ngtcp2_acktr_entry *ent;
+ const size_t extra = 17;
+ ngtcp2_log log;
+ ngtcp2_ksl_it it;
+
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ for (i = 0; i < NGTCP2_ACKTR_MAX_ENT + extra; ++i) {
+ ngtcp2_acktr_add(&acktr, (int64_t)(i * 2), 1, 999 + i);
+ }
+
+ CU_ASSERT(NGTCP2_ACKTR_MAX_ENT == ngtcp2_ksl_len(&acktr.ents));
+
+ for (i = 0, it = ngtcp2_acktr_get(&acktr); !ngtcp2_ksl_it_end(&it);
+ ++i, ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT((int64_t)((NGTCP2_ACKTR_MAX_ENT + extra - 1) * 2 - i * 2) ==
+ ent->pkt_num);
+ }
+
+ ngtcp2_acktr_free(&acktr);
+
+ /* Invert insertion order */
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ for (i = NGTCP2_ACKTR_MAX_ENT + extra; i > 0; --i) {
+ ngtcp2_acktr_add(&acktr, (int64_t)((i - 1) * 2), 1, 999 + i);
+ }
+
+ CU_ASSERT(NGTCP2_ACKTR_MAX_ENT == ngtcp2_ksl_len(&acktr.ents));
+
+ for (i = 0, it = ngtcp2_acktr_get(&acktr); !ngtcp2_ksl_it_end(&it);
+ ++i, ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT((int64_t)((NGTCP2_ACKTR_MAX_ENT + extra - 1) * 2 - i * 2) ==
+ ent->pkt_num);
+ }
+
+ ngtcp2_acktr_free(&acktr);
+}
+
+void test_ngtcp2_acktr_forget(void) {
+ ngtcp2_acktr acktr;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+ ngtcp2_acktr_entry *ent;
+ ngtcp2_log log;
+ ngtcp2_ksl_it it;
+
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ for (i = 0; i < 7; ++i) {
+ ngtcp2_acktr_add(&acktr, (int64_t)(i * 2), 1, 999 + i);
+ }
+
+ CU_ASSERT(7 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_acktr_get(&acktr);
+ ngtcp2_ksl_it_next(&it);
+ ngtcp2_ksl_it_next(&it);
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+ ngtcp2_acktr_forget(&acktr, ent);
+
+ CU_ASSERT(3 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(12 == ent->pkt_num);
+
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(10 == ent->pkt_num);
+
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(8 == ent->pkt_num);
+
+ it = ngtcp2_acktr_get(&acktr);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ ngtcp2_acktr_forget(&acktr, ent);
+
+ CU_ASSERT(0 == ngtcp2_ksl_len(&acktr.ents));
+
+ ngtcp2_acktr_free(&acktr);
+}
+
+void test_ngtcp2_acktr_recv_ack(void) {
+ ngtcp2_acktr acktr;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+ ngtcp2_ack ackfr;
+ int64_t rpkt_nums[] = {
+ 4500, 4499, 4497, 4496, 4494, 4493, 4491, 4490, 4488, 4487, 4483,
+ };
+ /*
+ 4500 4499
+ 4497 4496
+ 4494 4493
+ 4491 4490
+ 4488 4487
+ 4483
+ */
+ ngtcp2_acktr_entry *ent;
+ ngtcp2_log log;
+ ngtcp2_ksl_it it;
+
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+ ngtcp2_acktr_init(&acktr, &log, mem);
+
+ for (i = 0; i < ngtcp2_arraylen(rpkt_nums); ++i) {
+ ngtcp2_acktr_add(&acktr, rpkt_nums[i], 1, 999 + i);
+ }
+
+ CU_ASSERT(6 == ngtcp2_ksl_len(&acktr.ents));
+
+ ngtcp2_acktr_add_ack(&acktr, 998, 4497);
+ ngtcp2_acktr_add_ack(&acktr, 999, 4499);
+
+ ackfr.type = NGTCP2_FRAME_ACK;
+ ackfr.largest_ack = 998;
+ ackfr.ack_delay = 0;
+ ackfr.first_ack_range = 0;
+ ackfr.rangecnt = 0;
+
+ ngtcp2_acktr_recv_ack(&acktr, &ackfr);
+
+ CU_ASSERT(1 == ngtcp2_ringbuf_len(&acktr.acks));
+ CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_ksl_begin(&acktr.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(4500 == ent->pkt_num);
+ CU_ASSERT(2 == ent->len);
+
+ ackfr.type = NGTCP2_FRAME_ACK;
+ ackfr.largest_ack = 999;
+ ackfr.ack_delay = 0;
+ ackfr.first_ack_range = 0;
+ ackfr.rangecnt = 0;
+
+ ngtcp2_acktr_recv_ack(&acktr, &ackfr);
+
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&acktr.acks));
+ CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents));
+
+ it = ngtcp2_ksl_begin(&acktr.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(4500 == ent->pkt_num);
+ CU_ASSERT(1 == ent->len);
+
+ ngtcp2_acktr_free(&acktr);
+}
diff --git a/tests/ngtcp2_acktr_test.h b/tests/ngtcp2_acktr_test.h
new file mode 100644
index 0000000..6e9da5d
--- /dev/null
+++ b/tests/ngtcp2_acktr_test.h
@@ -0,0 +1,37 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ACKTR_TEST_H
+#define NGTCP2_ACKTR_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_acktr_add(void);
+void test_ngtcp2_acktr_eviction(void);
+void test_ngtcp2_acktr_forget(void);
+void test_ngtcp2_acktr_recv_ack(void);
+
+#endif /* NGTCP2_ACKTR_TEST_H */
diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c
new file mode 100644
index 0000000..f982aea
--- /dev/null
+++ b/tests/ngtcp2_conn_test.c
@@ -0,0 +1,9351 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_conn_test.h"
+
+#include <assert.h>
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_conn.h"
+#include "ngtcp2_test_helper.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_addr.h"
+#include "ngtcp2_net.h"
+
+static void qlog_write(void *user_data, uint32_t flags, const void *data,
+ size_t datalen) {
+ (void)user_data;
+ (void)flags;
+ (void)data;
+ (void)datalen;
+}
+
+static int null_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)dest;
+ (void)aead;
+ (void)aead_ctx;
+ (void)plaintext;
+ (void)plaintextlen;
+ (void)nonce;
+ (void)noncelen;
+ (void)aad;
+ (void)aadlen;
+
+ if (plaintextlen && plaintext != dest) {
+ memcpy(dest, plaintext, plaintextlen);
+ }
+ memset(dest + plaintextlen, 0, NGTCP2_FAKE_AEAD_OVERHEAD);
+
+ return 0;
+}
+
+static int null_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)dest;
+ (void)aead;
+ (void)aead_ctx;
+ (void)ciphertext;
+ (void)nonce;
+ (void)noncelen;
+ (void)aad;
+ (void)aadlen;
+ assert(ciphertextlen >= NGTCP2_FAKE_AEAD_OVERHEAD);
+ memmove(dest, ciphertext, ciphertextlen - NGTCP2_FAKE_AEAD_OVERHEAD);
+ return 0;
+}
+
+static int fail_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)dest;
+ (void)aead;
+ (void)aead_ctx;
+ (void)ciphertext;
+ (void)ciphertextlen;
+ (void)nonce;
+ (void)noncelen;
+ (void)aad;
+ (void)aadlen;
+ return NGTCP2_ERR_DECRYPT;
+}
+
+static int null_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ (void)hp;
+ (void)hp_ctx;
+ (void)sample;
+ memcpy(dest, NGTCP2_FAKE_HP_MASK, sizeof(NGTCP2_FAKE_HP_MASK) - 1);
+ return 0;
+}
+
+static int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data) {
+ (void)user_data;
+ memset(cid->data, 0, cidlen);
+ cid->data[0] = (uint8_t)(conn->scid.last_seq + 1);
+ cid->datalen = cidlen;
+ memset(token, 0, NGTCP2_STATELESS_RESET_TOKENLEN);
+ return 0;
+}
+
+static uint8_t null_secret[32];
+static uint8_t null_iv[16];
+static uint8_t null_data[4096];
+
+static ngtcp2_crypto_km null_ckm = {
+ {NULL, 0}, {0}, {null_iv, sizeof(null_iv)},
+ -1, 0, NGTCP2_CRYPTO_KM_FLAG_NONE,
+};
+
+static ngtcp2_path_storage null_path;
+static ngtcp2_path_storage new_path;
+static ngtcp2_path_storage new_nat_path;
+
+static ngtcp2_pkt_info null_pi;
+
+void init_static_path(void) {
+ path_init(&null_path, 0, 0, 0, 0);
+ path_init(&new_path, 1, 0, 2, 0);
+ path_init(&new_nat_path, 0, 0, 0, 1);
+}
+
+static ngtcp2_vec *null_datav(ngtcp2_vec *datav, size_t len) {
+ datav->base = null_data;
+ datav->len = len;
+ return datav;
+}
+
+static void init_crypto_ctx(ngtcp2_crypto_ctx *ctx) {
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->aead.max_overhead = NGTCP2_FAKE_AEAD_OVERHEAD;
+ ctx->max_encryption = 9999;
+ ctx->max_decryption_failure = 8888;
+}
+
+static void init_initial_crypto_ctx(ngtcp2_crypto_ctx *ctx) {
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->aead.max_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD;
+ ctx->max_encryption = 9999;
+ ctx->max_decryption_failure = 8888;
+}
+
+typedef struct {
+ uint64_t pkt_num;
+ /* stream_data is intended to store the arguments passed in
+ recv_stream_data callback. */
+ struct {
+ int64_t stream_id;
+ uint32_t flags;
+ size_t datalen;
+ } stream_data;
+ struct {
+ uint32_t flags;
+ const uint8_t *data;
+ size_t datalen;
+ uint64_t dgram_id;
+ } datagram;
+ struct {
+ uint32_t flags;
+ int64_t stream_id;
+ uint64_t app_error_code;
+ } stream_close;
+} my_user_data;
+
+static int client_initial(ngtcp2_conn *conn, void *user_data) {
+ (void)user_data;
+
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data,
+ 217);
+
+ return 0;
+}
+
+static int client_initial_early_data(ngtcp2_conn *conn, void *user_data) {
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ (void)user_data;
+
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data,
+ 217);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_early_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_early_key(conn, &aead_ctx, null_iv, sizeof(null_iv),
+ &hp_ctx);
+
+ return 0;
+}
+
+static int client_initial_large_crypto_early_data(ngtcp2_conn *conn,
+ void *user_data) {
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ (void)user_data;
+
+ /* Initial CRYPTO data which is larger than a typical single
+ datagram. */
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data,
+ 1500);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_early_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_early_key(conn, &aead_ctx, null_iv, sizeof(null_iv),
+ &hp_ctx);
+
+ return 0;
+}
+
+static int recv_client_initial_no_remote_transport_params(
+ ngtcp2_conn *conn, const ngtcp2_cid *dcid, void *user_data) {
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx ctx;
+
+ (void)dcid;
+ (void)user_data;
+
+ init_initial_crypto_ctx(&ctx);
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx);
+ ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx,
+ null_iv, &hp_ctx, sizeof(null_iv));
+
+ init_crypto_ctx(&ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ return 0;
+}
+
+static int recv_client_initial(ngtcp2_conn *conn, const ngtcp2_cid *dcid,
+ void *user_data) {
+ ngtcp2_transport_params params;
+
+ recv_client_initial_no_remote_transport_params(conn, dcid, user_data);
+
+ ngtcp2_transport_params_default(&params);
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+ ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ return 0;
+}
+
+static int recv_client_initial_early(ngtcp2_conn *conn, const ngtcp2_cid *dcid,
+ void *user_data) {
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx ctx;
+
+ recv_client_initial(conn, dcid, user_data);
+
+ init_crypto_ctx(&ctx);
+
+ ngtcp2_conn_set_early_crypto_ctx(conn, &ctx);
+ ngtcp2_conn_install_early_key(conn, &aead_ctx, null_iv, sizeof(null_iv),
+ &hp_ctx);
+
+ return 0;
+}
+
+static int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data) {
+ (void)conn;
+ (void)crypto_level;
+ (void)offset;
+ (void)data;
+ (void)datalen;
+ (void)user_data;
+ return 0;
+}
+
+static int recv_crypto_data_server_early_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset,
+ const uint8_t *data,
+ size_t datalen, void *user_data) {
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+
+ (void)offset;
+ (void)crypto_level;
+ (void)data;
+ (void)datalen;
+ (void)user_data;
+
+ assert(conn->server);
+
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data,
+ 179);
+
+ ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx,
+ null_iv, sizeof(null_iv), &hp_ctx);
+
+ conn->callbacks.recv_crypto_data = recv_crypto_data;
+
+ return 0;
+}
+
+static int update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret,
+ const uint8_t *current_tx_secret, size_t secretlen,
+ void *user_data) {
+ (void)conn;
+ (void)current_rx_secret;
+ (void)current_tx_secret;
+ (void)user_data;
+ (void)secretlen;
+
+ assert(sizeof(null_secret) == secretlen);
+
+ memset(rx_secret, 0xff, sizeof(null_secret));
+ memset(tx_secret, 0xff, sizeof(null_secret));
+ rx_aead_ctx->native_handle = NULL;
+ memset(rx_iv, 0xff, sizeof(null_iv));
+ tx_aead_ctx->native_handle = NULL;
+ memset(tx_iv, 0xff, sizeof(null_iv));
+
+ return 0;
+}
+
+static int recv_crypto_handshake_error(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data) {
+ (void)conn;
+ (void)crypto_level;
+ (void)offset;
+ (void)data;
+ (void)datalen;
+ (void)user_data;
+ return NGTCP2_ERR_CRYPTO;
+}
+
+static int recv_crypto_fatal_alert_generated(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset,
+ const uint8_t *data,
+ size_t datalen, void *user_data) {
+ (void)conn;
+ (void)crypto_level;
+ (void)offset;
+ (void)data;
+ (void)datalen;
+ (void)user_data;
+ return NGTCP2_ERR_CRYPTO;
+}
+
+static int recv_crypto_data_server(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data) {
+ (void)offset;
+ (void)data;
+ (void)datalen;
+ (void)user_data;
+
+ ngtcp2_conn_submit_crypto_data(conn,
+ crypto_level == NGTCP2_CRYPTO_LEVEL_INITIAL
+ ? NGTCP2_CRYPTO_LEVEL_INITIAL
+ : NGTCP2_CRYPTO_LEVEL_HANDSHAKE,
+ null_data, 218);
+
+ return 0;
+}
+
+static int recv_stream_data(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *data, size_t datalen,
+ void *user_data, void *stream_user_data) {
+ my_user_data *ud = user_data;
+ (void)conn;
+ (void)offset;
+ (void)data;
+ (void)stream_user_data;
+
+ if (ud) {
+ ud->stream_data.stream_id = stream_id;
+ ud->stream_data.flags = flags;
+ ud->stream_data.datalen = datalen;
+ }
+
+ return 0;
+}
+
+static int
+recv_stream_data_shutdown_stream_read(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *data, size_t datalen,
+ void *user_data, void *stream_user_data) {
+ int rv;
+
+ recv_stream_data(conn, flags, stream_id, offset, data, datalen, user_data,
+ stream_user_data);
+
+ rv = ngtcp2_conn_shutdown_stream_read(conn, stream_id, NGTCP2_APP_ERR01);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data) {
+ my_user_data *ud = user_data;
+ (void)conn;
+ (void)stream_user_data;
+
+ if (ud) {
+ ud->stream_close.flags = flags;
+ ud->stream_close.stream_id = stream_id;
+ ud->stream_close.app_error_code = app_error_code;
+ }
+
+ return 0;
+}
+
+static int recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
+ void *user_data) {
+ (void)conn;
+ (void)hd;
+ (void)user_data;
+ return 0;
+}
+
+static void genrand(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx) {
+ (void)rand_ctx;
+
+ memset(dest, 0, destlen);
+}
+
+static int recv_datagram(ngtcp2_conn *conn, uint32_t flags, const uint8_t *data,
+ size_t datalen, void *user_data) {
+ my_user_data *ud = user_data;
+ (void)conn;
+ (void)flags;
+
+ if (ud) {
+ ud->datagram.flags = flags;
+ ud->datagram.data = data;
+ ud->datagram.datalen = datalen;
+ }
+
+ return 0;
+}
+
+static int ack_datagram(ngtcp2_conn *conn, uint64_t dgram_id, void *user_data) {
+ my_user_data *ud = user_data;
+ (void)conn;
+
+ if (ud) {
+ ud->datagram.dgram_id = dgram_id;
+ }
+
+ return 0;
+}
+
+static int get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ memset(data, 0, NGTCP2_PATH_CHALLENGE_DATALEN);
+
+ return 0;
+}
+
+static int version_negotiation(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid, void *user_data) {
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ (void)client_dcid;
+ (void)user_data;
+
+ ngtcp2_conn_install_vneg_initial_key(conn, version, &aead_ctx, null_iv,
+ &hp_ctx, &aead_ctx, null_iv, &hp_ctx,
+ sizeof(null_iv));
+
+ return 0;
+}
+
+static void delete_crypto_aead_ctx(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx,
+ void *user_data) {
+ (void)conn;
+ (void)aead_ctx;
+ (void)user_data;
+}
+
+static void delete_crypto_cipher_ctx(ngtcp2_conn *conn,
+ ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ void *user_data) {
+ (void)conn;
+ (void)cipher_ctx;
+ (void)user_data;
+}
+
+static void server_default_settings(ngtcp2_settings *settings) {
+ memset(settings, 0, sizeof(*settings));
+ settings->log_printf = NULL;
+ settings->initial_ts = 0;
+ settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
+ settings->max_tx_udp_payload_size = 2048;
+ settings->no_tx_udp_payload_size_shaping = 1;
+ settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT;
+}
+
+static void server_default_transport_params(ngtcp2_transport_params *params) {
+ size_t i;
+
+ memset(params, 0, sizeof(*params));
+ params->initial_max_stream_data_bidi_local = 65535;
+ params->initial_max_stream_data_bidi_remote = 65535;
+ params->initial_max_stream_data_uni = 65535;
+ params->initial_max_data = 128 * 1024;
+ params->initial_max_streams_bidi = 3;
+ params->initial_max_streams_uni = 2;
+ params->max_idle_timeout = 60 * NGTCP2_SECONDS;
+ params->max_udp_payload_size = 65535;
+ params->stateless_reset_token_present = 1;
+ params->active_connection_id_limit = 8;
+ for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) {
+ params->stateless_reset_token[i] = (uint8_t)i;
+ }
+}
+
+static void server_default_callbacks(ngtcp2_callbacks *cb) {
+ memset(cb, 0, sizeof(*cb));
+ cb->recv_client_initial = recv_client_initial;
+ cb->recv_crypto_data = recv_crypto_data_server;
+ cb->decrypt = null_decrypt;
+ cb->encrypt = null_encrypt;
+ cb->hp_mask = null_hp_mask;
+ cb->rand = genrand;
+ cb->get_new_connection_id = get_new_connection_id;
+ cb->update_key = update_key;
+ cb->delete_crypto_aead_ctx = delete_crypto_aead_ctx;
+ cb->delete_crypto_cipher_ctx = delete_crypto_cipher_ctx;
+ cb->get_path_challenge_data = get_path_challenge_data;
+ cb->version_negotiation = version_negotiation;
+}
+
+static void server_early_callbacks(ngtcp2_callbacks *cb) {
+ server_default_callbacks(cb);
+
+ cb->recv_client_initial = recv_client_initial_early;
+ cb->recv_crypto_data = recv_crypto_data_server_early_data;
+}
+
+static void client_default_settings(ngtcp2_settings *settings) {
+ memset(settings, 0, sizeof(*settings));
+ settings->log_printf = NULL;
+ settings->initial_ts = 0;
+ settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
+ settings->max_tx_udp_payload_size = 2048;
+ settings->no_tx_udp_payload_size_shaping = 1;
+}
+
+static void client_default_transport_params(ngtcp2_transport_params *params) {
+ memset(params, 0, sizeof(*params));
+ params->initial_max_stream_data_bidi_local = 65535;
+ params->initial_max_stream_data_bidi_remote = 65535;
+ params->initial_max_stream_data_uni = 65535;
+ params->initial_max_data = 128 * 1024;
+ params->initial_max_streams_bidi = 0;
+ params->initial_max_streams_uni = 2;
+ params->max_idle_timeout = 60 * NGTCP2_SECONDS;
+ params->max_udp_payload_size = 65535;
+ params->stateless_reset_token_present = 0;
+ params->active_connection_id_limit = 8;
+}
+
+static void client_default_callbacks(ngtcp2_callbacks *cb) {
+ memset(cb, 0, sizeof(*cb));
+ cb->client_initial = client_initial;
+ cb->recv_crypto_data = recv_crypto_data;
+ cb->decrypt = null_decrypt;
+ cb->encrypt = null_encrypt;
+ cb->hp_mask = null_hp_mask;
+ cb->recv_retry = recv_retry;
+ cb->rand = genrand;
+ cb->get_new_connection_id = get_new_connection_id;
+ cb->update_key = update_key;
+ cb->delete_crypto_aead_ctx = delete_crypto_aead_ctx;
+ cb->delete_crypto_cipher_ctx = delete_crypto_cipher_ctx;
+ cb->get_path_challenge_data = get_path_challenge_data;
+ cb->version_negotiation = version_negotiation;
+}
+
+static void client_early_callbacks(ngtcp2_callbacks *cb) {
+ client_default_callbacks(cb);
+
+ cb->client_initial = client_initial_early_data;
+}
+
+static void conn_set_scid_used(ngtcp2_conn *conn) {
+ ngtcp2_scid *scid;
+ ngtcp2_ksl_it it;
+ int rv;
+ (void)rv;
+
+ assert(1 + (conn->local.transport_params.preferred_address_present != 0) ==
+ ngtcp2_ksl_len(&conn->scid.set));
+
+ it = ngtcp2_ksl_begin(&conn->scid.set);
+ scid = ngtcp2_ksl_it_get(&it);
+ scid->flags |= NGTCP2_SCID_FLAG_USED;
+
+ assert(NGTCP2_PQ_BAD_INDEX == scid->pe.index);
+
+ rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe);
+
+ assert(0 == rv);
+}
+
+static void
+setup_default_server_settings(ngtcp2_conn **pconn, const ngtcp2_path *path,
+ const ngtcp2_settings *settings,
+ const ngtcp2_transport_params *params) {
+ ngtcp2_callbacks cb;
+ ngtcp2_cid dcid, scid;
+ ngtcp2_transport_params remote_params;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ server_default_callbacks(&cb);
+
+ ngtcp2_conn_server_new(pconn, &dcid, &scid, path, NGTCP2_PROTO_VER_V1, &cb,
+ settings, params,
+ /* mem = */ NULL, NULL);
+ ngtcp2_conn_set_crypto_ctx(*pconn, &crypto_ctx);
+ ngtcp2_conn_install_rx_handshake_key(*pconn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_handshake_key(*pconn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_rx_key(*pconn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_key(*pconn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+ (*pconn)->state = NGTCP2_CS_POST_HANDSHAKE;
+ (*pconn)->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED;
+ (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ conn_set_scid_used(*pconn);
+ memset(&remote_params, 0, sizeof(remote_params));
+ remote_params.initial_max_stream_data_bidi_local = 64 * 1024;
+ remote_params.initial_max_stream_data_bidi_remote = 64 * 1024;
+ remote_params.initial_max_stream_data_uni = 64 * 1024;
+ remote_params.initial_max_streams_bidi = 0;
+ remote_params.initial_max_streams_uni = 1;
+ remote_params.initial_max_data = 64 * 1024;
+ remote_params.active_connection_id_limit = 8;
+ remote_params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ ngtcp2_transport_params_copy_new(&(*pconn)->remote.transport_params,
+ &remote_params, (*pconn)->mem);
+ (*pconn)->local.bidi.max_streams = remote_params.initial_max_streams_bidi;
+ (*pconn)->local.uni.max_streams = remote_params.initial_max_streams_uni;
+ (*pconn)->tx.max_offset = remote_params.initial_max_data;
+ (*pconn)->negotiated_version = (*pconn)->client_chosen_version;
+}
+
+static void setup_default_server(ngtcp2_conn **pconn) {
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+
+ server_default_settings(&settings);
+ server_default_transport_params(&params);
+
+ setup_default_server_settings(pconn, &null_path.path, &settings, &params);
+}
+
+static void setup_default_client(ngtcp2_conn **pconn) {
+ ngtcp2_callbacks cb;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ ngtcp2_cid dcid, scid;
+ ngtcp2_transport_params remote_params;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ client_default_callbacks(&cb);
+ client_default_settings(&settings);
+ client_default_transport_params(&params);
+
+ ngtcp2_conn_client_new(pconn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+ ngtcp2_conn_set_crypto_ctx(*pconn, &crypto_ctx);
+ ngtcp2_conn_install_rx_handshake_key(*pconn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_handshake_key(*pconn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_rx_key(*pconn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_key(*pconn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+ (*pconn)->state = NGTCP2_CS_POST_HANDSHAKE;
+ (*pconn)->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED;
+ (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ conn_set_scid_used(*pconn);
+ memset(&remote_params, 0, sizeof(remote_params));
+ remote_params.initial_max_stream_data_bidi_local = 64 * 1024;
+ remote_params.initial_max_stream_data_bidi_remote = 64 * 1024;
+ remote_params.initial_max_stream_data_uni = 64 * 1024;
+ remote_params.initial_max_streams_bidi = 1;
+ remote_params.initial_max_streams_uni = 1;
+ remote_params.initial_max_data = 64 * 1024;
+ remote_params.active_connection_id_limit = 8;
+ remote_params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ ngtcp2_transport_params_copy_new(&(*pconn)->remote.transport_params,
+ &remote_params, (*pconn)->mem);
+ (*pconn)->local.bidi.max_streams = remote_params.initial_max_streams_bidi;
+ (*pconn)->local.uni.max_streams = remote_params.initial_max_streams_uni;
+ (*pconn)->tx.max_offset = remote_params.initial_max_data;
+ (*pconn)->negotiated_version = (*pconn)->client_chosen_version;
+
+ (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ memset((*pconn)->dcid.current.token, 0xf1, NGTCP2_STATELESS_RESET_TOKENLEN);
+}
+
+static void setup_handshake_server(ngtcp2_conn **pconn) {
+ ngtcp2_callbacks cb;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ ngtcp2_cid dcid, scid;
+ uint32_t preferred_versions[] = {
+ NGTCP2_PROTO_VER_V2_DRAFT,
+ NGTCP2_PROTO_VER_V1,
+ };
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ server_default_callbacks(&cb);
+ server_default_settings(&settings);
+ server_default_transport_params(&params);
+
+ settings.preferred_versions = preferred_versions;
+ settings.preferred_versionslen = ngtcp2_arraylen(preferred_versions);
+
+ ngtcp2_conn_server_new(pconn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+}
+
+static void setup_handshake_client_version(ngtcp2_conn **pconn,
+ uint32_t client_chosen_version) {
+ ngtcp2_callbacks cb;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ ngtcp2_cid rcid, scid;
+ ngtcp2_crypto_aead retry_aead = {0, NGTCP2_FAKE_AEAD_OVERHEAD};
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+ uint32_t preferred_versions[] = {
+ NGTCP2_PROTO_VER_V2_DRAFT,
+ NGTCP2_PROTO_VER_V1,
+ };
+ uint32_t other_versions[] = {
+ NGTCP2_PROTO_VER_V1,
+ NGTCP2_PROTO_VER_V2_DRAFT,
+ };
+
+ rcid_init(&rcid);
+ scid_init(&scid);
+
+ init_initial_crypto_ctx(&crypto_ctx);
+
+ client_default_callbacks(&cb);
+ client_default_settings(&settings);
+ client_default_transport_params(&params);
+
+ settings.preferred_versions = preferred_versions;
+ settings.preferred_versionslen = ngtcp2_arraylen(preferred_versions);
+
+ settings.other_versions = other_versions;
+ settings.other_versionslen = ngtcp2_arraylen(other_versions);
+
+ ngtcp2_conn_client_new(pconn, &rcid, &scid, &null_path.path,
+ client_chosen_version, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+ ngtcp2_conn_set_initial_crypto_ctx(*pconn, &crypto_ctx);
+ ngtcp2_conn_install_initial_key(*pconn, &aead_ctx, null_iv, &hp_ctx,
+ &aead_ctx, null_iv, &hp_ctx, sizeof(null_iv));
+ ngtcp2_conn_set_retry_aead(*pconn, &retry_aead, &aead_ctx);
+}
+
+static void setup_handshake_client(ngtcp2_conn **pconn) {
+ setup_handshake_client_version(pconn, NGTCP2_PROTO_VER_V1);
+}
+
+static void setup_early_server(ngtcp2_conn **pconn) {
+ ngtcp2_callbacks cb;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ ngtcp2_cid dcid, scid;
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ server_early_callbacks(&cb);
+ server_default_settings(&settings);
+ server_default_transport_params(&params);
+
+ ngtcp2_conn_server_new(pconn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+}
+
+static void setup_early_client(ngtcp2_conn **pconn) {
+ ngtcp2_callbacks cb;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ ngtcp2_cid rcid, scid;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ rcid_init(&rcid);
+ scid_init(&scid);
+
+ init_initial_crypto_ctx(&crypto_ctx);
+
+ client_early_callbacks(&cb);
+ client_default_settings(&settings);
+ client_default_transport_params(&params);
+
+ ngtcp2_conn_client_new(pconn, &rcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+ ngtcp2_conn_set_initial_crypto_ctx(*pconn, &crypto_ctx);
+ ngtcp2_conn_install_initial_key(*pconn, &aead_ctx, null_iv, &hp_ctx,
+ &aead_ctx, null_iv, &hp_ctx, sizeof(null_iv));
+
+ memset(&params, 0, sizeof(params));
+ params.initial_max_stream_data_bidi_local = 64 * 1024;
+ params.initial_max_stream_data_bidi_remote = 64 * 1024;
+ params.initial_max_stream_data_uni = 64 * 1024;
+ params.initial_max_streams_bidi = 1;
+ params.initial_max_streams_uni = 1;
+ params.initial_max_data = 64 * 1024;
+ params.active_connection_id_limit = 8;
+ params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+
+ ngtcp2_conn_set_early_remote_transport_params(*pconn, &params);
+}
+
+void test_ngtcp2_conn_stream_open_close(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ int rv;
+ ngtcp2_frame fr;
+ ngtcp2_strm *strm;
+ int64_t stream_id;
+
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 17;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, NULL, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(NGTCP2_STRM_FLAG_NONE == strm->flags);
+
+ fr.stream.fin = 1;
+ fr.stream.offset = 17;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, NULL, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_STRM_FLAG_SHUT_RD == strm->flags);
+ 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, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_FIN, 4, NULL, 0, 3);
+
+ CU_ASSERT(spktlen > 0);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(NULL != strm);
+
+ /* Open a remote unidirectional stream */
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 2;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 19;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 3, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 2);
+
+ CU_ASSERT(NGTCP2_STRM_FLAG_SHUT_WR == strm->flags);
+ CU_ASSERT(fr.stream.data[0].len == strm->rx.last_offset);
+ CU_ASSERT(fr.stream.data[0].len == ngtcp2_strm_rx_offset(strm));
+
+ /* Open a local unidirectional stream */
+ rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(3 == stream_id);
+
+ rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_ID_BLOCKED == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_stream_rx_flow_control(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ int rv;
+ ngtcp2_frame fr;
+ ngtcp2_strm *strm;
+ size_t i;
+ int64_t stream_id;
+
+ setup_default_server(&conn);
+
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = 2047;
+
+ for (i = 0; i < 3; ++i) {
+ stream_id = (int64_t)(i * 4);
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = stream_id;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 1024;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, (int64_t)i, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(NULL != strm);
+
+ rv = ngtcp2_conn_extend_max_stream_offset(conn, stream_id,
+ fr.stream.data[0].len);
+
+ CU_ASSERT(0 == rv);
+ }
+
+ CU_ASSERT(3 == ngtcp2_pq_size(&conn->tx.strmq));
+
+ strm = ngtcp2_conn_find_stream(conn, 0);
+
+ CU_ASSERT(ngtcp2_strm_is_tx_queued(strm));
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(ngtcp2_strm_is_tx_queued(strm));
+
+ strm = ngtcp2_conn_find_stream(conn, 8);
+
+ CU_ASSERT(ngtcp2_strm_is_tx_queued(strm));
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(ngtcp2_pq_empty(&conn->tx.strmq));
+
+ for (i = 0; i < 3; ++i) {
+ stream_id = (int64_t)(i * 4);
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(2047 + 1024 == strm->rx.max_offset);
+ }
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_stream_rx_flow_control_error(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ int rv;
+ ngtcp2_frame fr;
+
+ setup_default_server(&conn);
+
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = 1023;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 1024;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_stream_tx_flow_control(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ int rv;
+ ngtcp2_frame fr;
+ ngtcp2_strm *strm;
+ ngtcp2_ssize nwrite;
+ int64_t stream_id;
+
+ setup_default_client(&conn);
+
+ conn->remote.transport_params->initial_max_stream_data_bidi_remote = 2047;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, 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, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, 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, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, 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, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 0, 3);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 == nwrite);
+ CU_ASSERT(2047 == strm->tx.offset);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.max_stream_data.stream_id = stream_id;
+ fr.max_stream_data.max_stream_data = 2048;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2048 == strm->tx.max_offset);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 1024, 5);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(1 == nwrite);
+ CU_ASSERT(2048 == strm->tx.offset);
+
+ ngtcp2_conn_del(conn);
+
+ /* CWND left is round up to the maximum UDP packet size */
+ setup_default_client(&conn);
+
+ conn->cstat.cwnd = 1;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_FIN,
+ stream_id, null_data, 1024, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(1024 == nwrite);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_rx_flow_control(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ int rv;
+ ngtcp2_frame fr;
+
+ setup_default_server(&conn);
+
+ conn->local.transport_params.initial_max_data = 1024;
+ conn->rx.window = 1024;
+ conn->rx.max_offset = 1024;
+ conn->rx.unsent_max_offset = 1024;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 1023;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_extend_max_offset(conn, 1023);
+
+ CU_ASSERT(1024 + 1023 == conn->rx.unsent_max_offset);
+ CU_ASSERT(1024 == conn->rx.max_offset);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 1023;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 1;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_extend_max_offset(conn, 1);
+
+ CU_ASSERT(2048 == conn->rx.unsent_max_offset);
+ CU_ASSERT(1024 == conn->rx.max_offset);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(2048 == conn->rx.max_offset);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_rx_flow_control_error(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ int rv;
+ ngtcp2_frame fr;
+
+ setup_default_server(&conn);
+
+ conn->local.transport_params.initial_max_data = 1024;
+ conn->rx.window = 1024;
+ conn->rx.max_offset = 1024;
+ conn->rx.unsent_max_offset = 1024;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 1025;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_tx_flow_control(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ int rv;
+ ngtcp2_frame fr;
+ ngtcp2_ssize nwrite;
+ int64_t stream_id;
+
+ setup_default_client(&conn);
+
+ conn->remote.transport_params->initial_max_data = 2048;
+ conn->tx.max_offset = 2048;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, 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, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, 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, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, 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, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 1024, 4);
+
+ CU_ASSERT(spktlen == 0);
+ CU_ASSERT(-1 == nwrite);
+
+ fr.type = NGTCP2_FRAME_MAX_DATA;
+ fr.max_data.max_data = 3072;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(3072 == conn->tx.max_offset);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 1024, 4);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(1024 == nwrite);
+ CU_ASSERT(3072 == conn->tx.offset);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_shutdown_stream_write(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ ngtcp2_frame_chain *frc;
+ uint8_t buf[2048];
+ ngtcp2_frame fr;
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_strm *strm;
+ int64_t stream_id;
+
+ /* Stream not found */
+ setup_default_server(&conn);
+
+ rv = ngtcp2_conn_shutdown_stream_write(conn, 4, NGTCP2_APP_ERR01);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Check final_size */
+ setup_default_client(&conn);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data,
+ 1239, 1);
+ rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01);
+
+ CU_ASSERT(0 == rv);
+
+ for (frc = conn->pktns.tx.frq; frc; frc = frc->next) {
+ if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) {
+ break;
+ }
+ }
+
+ CU_ASSERT(NULL != frc);
+ CU_ASSERT(stream_id == frc->fr.reset_stream.stream_id);
+ CU_ASSERT(NGTCP2_APP_ERR01 == frc->fr.reset_stream.app_error_code);
+ CU_ASSERT(1239 == frc->fr.reset_stream.final_size);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(NULL != strm);
+ CU_ASSERT(NGTCP2_APP_ERR01 == strm->app_error_code);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = stream_id;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 100;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 890, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 899, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id));
+
+ ngtcp2_conn_del(conn);
+
+ /* Check that stream is closed when RESET_STREAM is acknowledged */
+ setup_default_client(&conn);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = stream_id;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 119, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
+
+ rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* Incoming FIN does not close stream */
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 121, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 332, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id));
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM is not sent if all tx data are acknowledged */
+ setup_default_client(&conn);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id,
+ null_data, 0, 3);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 999, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 7);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST));
+
+ spktlen =
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_FIN, -1, NULL, 0, 11);
+
+ CU_ASSERT(0 == spktlen);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_reset_stream(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ uint8_t buf[2048];
+ ngtcp2_frame fr;
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_strm *strm;
+ int64_t stream_id;
+
+ /* Receive RESET_STREAM */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 955;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD);
+ CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_RECV_RST);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receive RESET_STREAM after sending STOP_SENDING */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
+ ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01);
+ ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 955;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+
+ ngtcp2_conn_del(conn);
+
+ /* Receive RESET_STREAM after sending RESET_STREAM */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
+ ngtcp2_conn_shutdown_stream_write(conn, 4, NGTCP2_APP_ERR01);
+ ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 955;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 3, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4));
+
+ ngtcp2_conn_del(conn);
+
+ /* Receive RESET_STREAM after receiving STOP_SENDING */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = 4;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 4);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 955;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 3, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 4, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4));
+
+ ngtcp2_conn_del(conn);
+
+ /* final_size in RESET_STREAM exceeds the already received offset */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 954;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(NGTCP2_ERR_FINAL_SIZE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* final_size in RESET_STREAM differs from the final offset which
+ STREAM frame with fin indicated. */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 956;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(NGTCP2_ERR_FINAL_SIZE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM against local stream which has not been initiated. */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 1;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM against remote stream which has not been initiated */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 0;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = 1999;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 0));
+ CU_ASSERT(4 == conn->remote.bidi.unsent_max_streams);
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM against remote stream which is larger than allowed
+ maximum */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 16;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM against remote stream which is allowed, and no
+ ngtcp2_strm object has been created */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(
+ ngtcp2_idtr_is_open(&conn->remote.bidi.idtr, fr.reset_stream.stream_id));
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM against remote stream which is allowed, and no
+ ngtcp2_strm object has been created, and final_size violates
+ connection-level flow control. */
+ setup_default_server(&conn);
+
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = 1 << 21;
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = 1 << 20;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM against remote stream which is allowed, and no
+ ngtcp2_strm object has been created, and final_size violates
+ stream-level flow control. */
+ setup_default_server(&conn);
+
+ conn->rx.max_offset = 1 << 21;
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = 1 << 20;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* final_size in RESET_STREAM violates connection-level flow
+ control */
+ setup_default_server(&conn);
+
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = 1 << 21;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 1024 * 1024;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* final_size in RESET_STREAM violates stream-level flow control */
+ setup_default_server(&conn);
+
+ conn->rx.max_offset = 1 << 21;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 1024 * 1024;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving RESET_STREAM for a local unidirectional stream is a
+ protocol violation. */
+ setup_default_server(&conn);
+
+ rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = stream_id;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_PROTO == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* RESET_STREAM extends connection window including buffered data */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 1;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 1024;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1024 == conn->rx.offset);
+ CU_ASSERT(128 * 1024 + 1024 == conn->rx.unsent_max_offset);
+
+ /* Receiving same RESET_STREAM does not increase rx offsets. */
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1024 == conn->rx.offset);
+ CU_ASSERT(128 * 1024 + 1024 == conn->rx.unsent_max_offset);
+
+ ngtcp2_conn_del(conn);
+
+ /* Verify that connection window is properly updated when
+ RESET_STREAM is received after sending STOP_SENDING */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 1;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 955;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
+ ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01);
+ ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
+
+ CU_ASSERT(128 * 1024 + 956 == conn->rx.unsent_max_offset);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR02;
+ fr.reset_stream.final_size = 957;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+ CU_ASSERT(128 * 1024 + 956 + 1 == conn->rx.unsent_max_offset);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_stop_sending(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ uint8_t buf[2048];
+ ngtcp2_frame fr;
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_strm *strm;
+ ngtcp2_tstamp t = 0;
+ int64_t pkt_num = 0;
+ ngtcp2_frame_chain *frc;
+ int64_t stream_id;
+
+ /* Receive STOP_SENDING */
+ setup_default_client(&conn);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data,
+ 333, ++t);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = stream_id;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR);
+ CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SENT_RST);
+
+ for (frc = conn->pktns.tx.frq; frc; frc = frc->next) {
+ if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) {
+ break;
+ }
+ }
+
+ CU_ASSERT(NULL != frc);
+ CU_ASSERT(NGTCP2_APP_ERR01 == frc->fr.reset_stream.app_error_code);
+ CU_ASSERT(333 == frc->fr.reset_stream.final_size);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ for (frc = conn->pktns.tx.frq; frc; frc = frc->next) {
+ if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) {
+ break;
+ }
+ }
+
+ CU_ASSERT(NULL == frc);
+
+ /* Make sure that receiving duplicated STOP_SENDING does not trigger
+ another RESET_STREAM. */
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR);
+ CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SENT_RST);
+
+ for (frc = conn->pktns.tx.frq; frc; frc = frc->next) {
+ if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) {
+ break;
+ }
+ }
+
+ CU_ASSERT(NULL == frc);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receive STOP_SENDING after receiving RESET_STREAM */
+ setup_default_client(&conn);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data,
+ 333, ++t);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = stream_id;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = stream_id;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
+
+ for (frc = conn->pktns.tx.frq; frc; frc = frc->next) {
+ if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) {
+ break;
+ }
+ }
+
+ CU_ASSERT(NULL != frc);
+ CU_ASSERT(NGTCP2_APP_ERR01 == frc->fr.reset_stream.app_error_code);
+ CU_ASSERT(333 == frc->fr.reset_stream.final_size);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id));
+
+ ngtcp2_conn_del(conn);
+
+ /* STOP_SENDING against remote bidirectional stream which has not
+ been initiated. */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = 0;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 0);
+
+ CU_ASSERT(NULL != strm);
+ CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR);
+
+ ngtcp2_conn_del(conn);
+
+ /* STOP_SENDING against local bidirectional stream which has not
+ been initiated. */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = 1;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving STOP_SENDING for a local unidirectional stream */
+ setup_default_server(&conn);
+
+ rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = stream_id;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_FRAME_RESET_STREAM == conn->pktns.tx.frq->fr.type);
+
+ ngtcp2_conn_del(conn);
+
+ /* STOP_SENDING against local unidirectional stream which has not
+ been initiated. */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = 3;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* STOP_SENDING against local bidirectional stream in Data Sent
+ state. Because all data have been acknowledged, and FIN is sent,
+ RESET_STREAM is not necessary. */
+ setup_default_client(&conn);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id,
+ null_data, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stop_sending.stream_id = stream_id;
+ fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == conn->pktns.tx.frq);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_conn_id_omitted(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ uint8_t buf[2048];
+ ngtcp2_frame fr;
+ size_t pktlen;
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 100;
+ fr.stream.data[0].base = null_data;
+
+ /* Receiving packet which has no connection ID while SCID of server
+ is not empty. */
+ setup_default_server(&conn);
+
+ pktlen = write_pkt(buf, sizeof(buf), /* dcid = */ NULL, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ /* packet is just ignored */
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4));
+
+ ngtcp2_conn_del(conn);
+
+ /* Allow omission of connection ID */
+ setup_default_server(&conn);
+ ngtcp2_cid_zero(&conn->oscid);
+
+ it = ngtcp2_ksl_begin(&conn->scid.set);
+ scid = ngtcp2_ksl_it_get(&it);
+ ngtcp2_cid_zero(&scid->cid);
+
+ pktlen = write_pkt(buf, sizeof(buf), /* dcid = */ NULL, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_short_pkt_type(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_pkt_hd hd;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ int64_t stream_id;
+
+ /* 1 octet pkt num */
+ setup_default_client(&conn);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen,
+ conn->oscid.datalen) > 0);
+ CU_ASSERT(1 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 2 octets pkt num */
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 0x6afa2f;
+ conn->pktns.tx.last_pkt_num = 0x6afd78;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen,
+ conn->oscid.datalen) > 0);
+ CU_ASSERT(2 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 4 octets pkt num */
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 0x6afa2f;
+ conn->pktns.tx.last_pkt_num = 0x6bc106;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen,
+ conn->oscid.datalen) > 0);
+ CU_ASSERT(3 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 1 octet pkt num (largest)*/
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1;
+ conn->pktns.tx.last_pkt_num = 128;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen,
+ conn->oscid.datalen) > 0);
+ CU_ASSERT(1 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 2 octet pkt num (shortest)*/
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1;
+ conn->pktns.tx.last_pkt_num = 129;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen,
+ conn->oscid.datalen) > 0);
+ CU_ASSERT(2 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 2 octet pkt num (largest)*/
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1;
+ conn->pktns.tx.last_pkt_num = 32768;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(
+ pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0);
+ CU_ASSERT(2 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 3 octet pkt num (shortest) */
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1;
+ conn->pktns.tx.last_pkt_num = 32769;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(
+ pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0);
+ CU_ASSERT(3 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 3 octet pkt num (largest) */
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1;
+ conn->pktns.tx.last_pkt_num = 8388608;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(
+ pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0);
+ CU_ASSERT(3 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 4 octet pkt num (shortest)*/
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1;
+ conn->pktns.tx.last_pkt_num = 8388609;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(
+ pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0);
+ CU_ASSERT(4 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Overflow */
+ setup_default_client(&conn);
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1;
+ conn->pktns.tx.last_pkt_num = NGTCP2_MAX_PKT_NUM - 1;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 19, 1);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(
+ pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0);
+ CU_ASSERT(4 == hd.pkt_numlen);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_stateless_reset(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[256];
+ ngtcp2_ssize spktlen;
+ int rv;
+ size_t i;
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+
+ for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) {
+ token[i] = (uint8_t)~i;
+ }
+
+ /* server */
+ setup_default_server(&conn);
+ conn->callbacks.decrypt = fail_decrypt;
+ conn->pktns.rx.max_pkt_num = 24324325;
+
+ ngtcp2_dcid_set_token(&conn->dcid.current, token);
+
+ spktlen = ngtcp2_pkt_write_stateless_reset(
+ buf, sizeof(buf), token, null_data, NGTCP2_MIN_STATELESS_RESET_RANDLEN);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
+ CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
+
+ ngtcp2_conn_del(conn);
+
+ /* client */
+ setup_default_client(&conn);
+ conn->callbacks.decrypt = fail_decrypt;
+ conn->pktns.rx.max_pkt_num = 3255454;
+
+ ngtcp2_dcid_set_token(&conn->dcid.current, token);
+
+ spktlen =
+ ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), token, null_data, 29);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
+ CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
+
+ ngtcp2_conn_del(conn);
+
+ /* stateless reset in long packet */
+ setup_default_server(&conn);
+ conn->callbacks.decrypt = fail_decrypt;
+ conn->pktns.rx.max_pkt_num = 754233;
+
+ ngtcp2_dcid_set_token(&conn->dcid.current, token);
+
+ spktlen = ngtcp2_pkt_write_stateless_reset(
+ buf, sizeof(buf), token, null_data, NGTCP2_MIN_STATELESS_RESET_RANDLEN);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* long packet */
+ buf[0] |= NGTCP2_HEADER_FORM_BIT;
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
+ CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
+
+ ngtcp2_conn_del(conn);
+
+ /* stateless reset in long packet; parsing long header fails */
+ setup_default_server(&conn);
+ conn->callbacks.decrypt = fail_decrypt;
+ conn->pktns.rx.max_pkt_num = 754233;
+
+ ngtcp2_dcid_set_token(&conn->dcid.current, token);
+
+ spktlen = ngtcp2_pkt_write_stateless_reset(
+ buf, 41, token, null_data, NGTCP2_MIN_STATELESS_RESET_RANDLEN);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* long packet */
+ buf[0] |= NGTCP2_HEADER_FORM_BIT;
+ buf[0] |= 0x30;
+ /* Make version nonzero so that it does not look like Version
+ Negotiation packet */
+ buf[1] = 0xff;
+ /* Make largest CID so that ngtcp2_pkt_decode_hd_long fails */
+ buf[5] = 0xff;
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, 1);
+
+ CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
+ CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
+
+ ngtcp2_conn_del(conn);
+
+ /* token does not match */
+ setup_default_client(&conn);
+ conn->callbacks.decrypt = fail_decrypt;
+ conn->pktns.rx.max_pkt_num = 24324325;
+
+ spktlen =
+ ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), token, null_data, 29);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_CS_DRAINING != conn->state);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_retry(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ uint64_t t = 0;
+ ngtcp2_cid dcid;
+ const uint8_t token[] = "address-validation-token";
+ size_t i;
+ int64_t stream_id;
+ ngtcp2_ssize datalen;
+ int rv;
+ ngtcp2_vec datav;
+ ngtcp2_strm *strm;
+ ngtcp2_crypto_aead aead = {0};
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+
+ dcid_init(&dcid);
+ setup_handshake_client(&conn);
+ conn->callbacks.recv_retry = recv_retry;
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ spktlen = ngtcp2_pkt_write_retry(
+ buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &conn->oscid, &dcid,
+ ngtcp2_conn_get_dcid(conn), token, strsize(token), null_encrypt, &aead,
+ &aead_ctx);
+
+ CU_ASSERT(spktlen > 0);
+
+ for (i = 0; i < 2; ++i) {
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ if (i == 1) {
+ /* Retry packet was ignored */
+ CU_ASSERT(spktlen == 0);
+ } else {
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(1 == conn->in_pktns->tx.last_pkt_num);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, ngtcp2_conn_get_dcid(conn)));
+ CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY);
+ }
+ }
+
+ ngtcp2_conn_del(conn);
+
+ /* Retry packet with non-matching tag is rejected */
+ setup_handshake_client(&conn);
+ conn->callbacks.recv_retry = recv_retry;
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ spktlen = ngtcp2_pkt_write_retry(
+ buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &conn->oscid, &dcid,
+ ngtcp2_conn_get_dcid(conn), token, strsize(token), null_encrypt, &aead,
+ &aead_ctx);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* Change tag */
+ buf[spktlen - 1] = 1;
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Make sure that 0RTT packets are retransmitted */
+ setup_early_client(&conn);
+ conn->callbacks.recv_retry = recv_retry;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen =
+ ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_datav(&datav, 219), 1, ++t);
+
+ CU_ASSERT(sizeof(buf) == spktlen);
+ CU_ASSERT(219 == datalen);
+
+ spktlen =
+ ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_datav(&datav, 119), 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(119 == datalen);
+
+ spktlen = ngtcp2_pkt_write_retry(
+ buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &conn->oscid, &dcid,
+ ngtcp2_conn_get_dcid(conn), token, strsize(token), null_encrypt, &aead,
+ &aead_ctx);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ /* Make sure that resent 0RTT packet is padded */
+ CU_ASSERT(sizeof(buf) == spktlen);
+ CU_ASSERT(2 == conn->pktns.tx.last_pkt_num);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(0 == ngtcp2_ksl_len(strm->tx.streamfrq));
+
+ /* ngtcp2_conn_write_stream sends new 0RTT packet. */
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 120, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(3 == conn->pktns.tx.last_pkt_num);
+ CU_ASSERT(120 == datalen);
+ CU_ASSERT(NULL == conn->pktns.tx.frq);
+ CU_ASSERT(!ngtcp2_rtb_empty(&conn->pktns.rtb));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_delayed_handshake_pkt(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_frame fr;
+ int rv;
+
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 567;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), 1,
+ NGTCP2_PROTO_VER_V1, &fr, 1, &null_ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == ngtcp2_ksl_len(&conn->hs_pktns->acktr.ents));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_max_streams(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ int rv;
+ ngtcp2_frame fr;
+
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI;
+ fr.max_streams.max_streams = 999;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(999 == conn->local.uni.max_streams);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI;
+ fr.max_streams.max_streams = 997;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(997 == conn->local.bidi.max_streams);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_handshake(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_frame fr;
+ int64_t pkt_num = 12345689;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_cid rcid;
+ int rv;
+ int64_t stream_id;
+ ngtcp2_ssize nwrite;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ rcid_init(&rcid);
+
+ /* Make sure server Initial is padded */
+ setup_handshake_server(&conn);
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen >= 1200);
+
+ ngtcp2_conn_del(conn);
+
+ /* Make sure server Handshake is padded when ack-eliciting Initial
+ is coalesced. */
+ setup_handshake_server(&conn);
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data,
+ 91);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen >= 1200);
+ CU_ASSERT(1 == ngtcp2_ksl_len(&conn->hs_pktns->rtb.ents));
+
+ ngtcp2_conn_del(conn);
+
+ /* Make sure that client packet is padded if it includes Initial and
+ 0RTT packets */
+ setup_early_client(&conn);
+
+ conn->callbacks.client_initial = client_initial_large_crypto_early_data;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ /* First packet should only includes Initial. No space for 0RTT. */
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, 1280, &nwrite,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 10, ++t);
+
+ CU_ASSERT(1280 == spktlen);
+ CU_ASSERT(-1 == nwrite);
+
+ /* Second packet has a room for 0RTT. */
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, 1280, &nwrite,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 10, ++t);
+
+ CU_ASSERT(1280 == spktlen);
+ CU_ASSERT(10 == nwrite);
+
+ /* We have no data to send. */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, 1280, ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Make sure that client non ack-eliciting Initial triggers
+ padding. */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen >= 1200);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ init_crypto_ctx(&crypto_ctx);
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen >= 1200);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_handshake_error(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_frame fr;
+ int64_t pkt_num = 107;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_cid rcid;
+ int rv;
+
+ rcid_init(&rcid);
+
+ /* client side */
+ setup_handshake_client(&conn);
+ conn->callbacks.recv_crypto_data = recv_crypto_handshake_error;
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 333;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_CRYPTO == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* server side */
+ setup_handshake_server(&conn);
+ conn->callbacks.recv_crypto_data = recv_crypto_handshake_error;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_CRYPTO == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* server side; wrong version */
+ setup_handshake_server(&conn);
+ conn->callbacks.recv_crypto_data = recv_crypto_handshake_error;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1201;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen =
+ write_initial_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn),
+ ++pkt_num, 0xffff, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_retransmit_protected(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 0;
+ int64_t stream_id, stream_id_a, stream_id_b;
+ ngtcp2_ksl_it it;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ ngtcp2_vec datav;
+ int accepted;
+ int rv;
+ ngtcp2_strm *strm;
+
+ /* Retransmit a packet completely */
+ setup_default_client(&conn);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 126, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* Kick delayed ACK timer */
+ t += NGTCP2_SECONDS;
+
+ conn->pktns.tx.last_pkt_num = 1000000009;
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007;
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+ ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t);
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ CU_ASSERT(1 == strm->tx.loss_count);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(NULL == conn->pktns.tx.frq);
+
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+
+ CU_ASSERT(!ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_conn_del(conn);
+
+ /* Retransmission takes place per frame basis. */
+ setup_default_client(&conn);
+ conn->local.bidi.max_streams = 3;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id_a, NULL);
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id_b, NULL);
+
+ ngtcp2_conn_shutdown_stream_write(conn, stream_id_a, NGTCP2_APP_ERR01);
+ ngtcp2_conn_shutdown_stream_write(conn, stream_id_b, NGTCP2_APP_ERR01);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* Kick delayed ACK timer */
+ t += NGTCP2_SECONDS;
+
+ conn->pktns.tx.last_pkt_num = 1000000009;
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007;
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+ ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t);
+ spktlen =
+ ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, (size_t)(spktlen - 1), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+
+ CU_ASSERT(!ngtcp2_ksl_it_end(&it));
+ CU_ASSERT(NULL != conn->pktns.tx.frq);
+
+ ngtcp2_conn_del(conn);
+
+ /* DATAGRAM frame must not be retransmitted */
+ setup_default_client(&conn);
+
+ conn->callbacks.ack_datagram = ack_datagram;
+ conn->remote.transport_params->max_datagram_frame_size = 65535;
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ datav.base = null_data;
+ datav.len = 99;
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 1000000009, &datav, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* Kick delayed ACK timer */
+ t += NGTCP2_SECONDS;
+
+ conn->pktns.tx.last_pkt_num = 1000000009;
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007;
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+ ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t);
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+ CU_ASSERT(NULL == conn->pktns.tx.frq);
+
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+
+ CU_ASSERT(!ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_conn_del(conn);
+
+ /* Retransmit an empty STREAM frame */
+ setup_default_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ NULL, 0, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* Kick delayed ACK timer */
+ t += NGTCP2_SECONDS;
+
+ conn->pktns.tx.last_pkt_num = 1000000009;
+ conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007;
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+ ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t);
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(NULL == conn->pktns.tx.frq);
+
+ it = ngtcp2_rtb_head(&conn->pktns.rtb);
+
+ CU_ASSERT(!ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_send_max_stream_data(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_strm *strm;
+ int64_t pkt_num = 890;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_frame fr;
+ int rv;
+ const uint32_t datalen = 1024;
+
+ /* MAX_STREAM_DATA should be sent */
+ setup_default_server(&conn);
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = datalen;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(ngtcp2_strm_is_tx_queued(strm));
+
+ ngtcp2_conn_del(conn);
+
+ /* MAX_STREAM_DATA should not be sent on incoming fin */
+ setup_default_server(&conn);
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = datalen;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(!ngtcp2_strm_is_tx_queued(strm));
+
+ ngtcp2_conn_del(conn);
+
+ /* MAX_STREAM_DATA should not be sent if STOP_SENDING frame is being
+ sent by local endpoint */
+ setup_default_server(&conn);
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = datalen;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(!ngtcp2_strm_is_tx_queued(strm));
+
+ ngtcp2_conn_del(conn);
+
+ /* MAX_STREAM_DATA should not be sent if stream is being reset by
+ remote endpoint */
+ setup_default_server(&conn);
+ conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = datalen;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 4;
+ fr.reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ fr.reset_stream.final_size = datalen;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_pq_empty(&conn->tx.strmq));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_stream_data(void) {
+ uint8_t buf[1024];
+ ngtcp2_conn *conn;
+ my_user_data ud;
+ int64_t pkt_num = 612;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ int rv;
+ int64_t stream_id;
+ size_t i;
+
+ /* 2 STREAM frames are received in the correct order. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 111;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN));
+ CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_EARLY));
+ CU_ASSERT(111 == ud.stream_data.datalen);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 111;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 99;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN);
+ CU_ASSERT(99 == ud.stream_data.datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 2 STREAM frames are received in the correct order, and 2nd STREAM
+ frame has 0 length, and FIN bit set. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 111;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN));
+ CU_ASSERT(111 == ud.stream_data.datalen);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 111;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN);
+ CU_ASSERT(0 == ud.stream_data.datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 2 identical STREAM frames with FIN bit set are received. The
+ recv_stream_data callback should not be called for sencond STREAM
+ frame. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 111;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN);
+ CU_ASSERT(111 == ud.stream_data.datalen);
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ud.stream_data.stream_id);
+ CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN));
+ CU_ASSERT(0 == ud.stream_data.datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Re-ordered STREAM frame; we first gets 0 length STREAM frame with
+ FIN bit set. Then the remaining STREAM frame is received. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 599;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ud.stream_data.stream_id);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 599;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN);
+ CU_ASSERT(599 == ud.stream_data.datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Simulate the case where packet is lost. We first gets 0 length
+ STREAM frame with FIN bit set. Then the lost STREAM frame is
+ retransmitted with FIN bit set is received. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 599;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ud.stream_data.stream_id);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 599;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN);
+ CU_ASSERT(599 == ud.stream_data.datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receive an unidirectional stream data */
+ setup_default_client(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 3;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 911;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(3 == ud.stream_data.stream_id);
+ CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN));
+ CU_ASSERT(911 == ud.stream_data.datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receive an unidirectional stream which is beyond the limit. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->remote.uni.max_streams = 0;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 2;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 911;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving nonzero payload for an local unidirectional stream is a
+ protocol violation. */
+ setup_default_client(&conn);
+
+ rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = stream_id;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 9;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* DATA on crypto stream, and TLS alert is generated. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_crypto_data = recv_crypto_fatal_alert_generated;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 139;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_CRYPTO == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* 0 length STREAM frame is allowed */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+
+ ngtcp2_conn_del(conn);
+
+ /* After sending STOP_SENDING, receiving 2 STREAM frames with fin
+ bit set must not invoke recv_stream_data callback. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
+
+ rv = ngtcp2_conn_shutdown_stream_read(conn, 4, 99);
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < 2; ++i) {
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].base = null_data;
+ fr.stream.data[0].len = 19;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ ud.stream_data.stream_id = 0;
+ rv =
+ ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ud.stream_data.stream_id);
+ CU_ASSERT(19 == conn->rx.offset);
+ CU_ASSERT(19 == conn->rx.unsent_max_offset -
+ conn->local.transport_params.initial_max_data);
+ CU_ASSERT(conn->local.transport_params.initial_max_data ==
+ conn->rx.max_offset);
+ }
+
+ ngtcp2_conn_del(conn);
+
+ /* After receiving RESET_STREAM, recv_stream_data callback must not
+ be invoked */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 0;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 0));
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.reset_stream.stream_id = 0;
+ fr.reset_stream.app_error_code = 999;
+ fr.reset_stream.final_size = 199;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 0));
+ CU_ASSERT(199 == conn->rx.unsent_max_offset -
+ conn->local.transport_params.initial_max_data);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 0;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].base = null_data;
+ fr.stream.data[0].len = 198;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ ud.stream_data.stream_id = -1;
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(-1 == ud.stream_data.stream_id);
+ CU_ASSERT(199 == conn->rx.unsent_max_offset -
+ conn->local.transport_params.initial_max_data);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 0;
+ fr.stream.fin = 1;
+ fr.stream.offset = 198;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].base = null_data;
+ fr.stream.data[0].len = 1;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ ud.stream_data.stream_id = -1;
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(-1 == ud.stream_data.stream_id);
+ CU_ASSERT(199 == conn->rx.unsent_max_offset -
+ conn->local.transport_params.initial_max_data);
+
+ ngtcp2_conn_del(conn);
+
+ /* ngtcp2_conn_shutdown_stream_read is called in recv_stream_data
+ callback. Further recv_stream_data callback must not be
+ called. */
+ setup_default_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data_shutdown_stream_read;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 599;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 1;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ud.stream_data.stream_id);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 599;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN));
+ CU_ASSERT(599 == ud.stream_data.datalen);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_ping(void) {
+ uint8_t buf[1024];
+ ngtcp2_conn *conn;
+ int64_t pkt_num = 133;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ int rv;
+
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_PING;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == conn->pktns.tx.frq);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_max_stream_data(void) {
+ uint8_t buf[1024];
+ ngtcp2_conn *conn;
+ int64_t pkt_num = 1000000007;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ int rv;
+ ngtcp2_strm *strm;
+
+ /* Receiving MAX_STREAM_DATA to an uninitiated local bidirectional
+ stream ID is an error */
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.max_stream_data.stream_id = 4;
+ fr.max_stream_data.max_stream_data = 8092;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving MAX_STREAM_DATA to an uninitiated local unidirectional
+ stream ID is an error */
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.max_stream_data.stream_id = 2;
+ fr.max_stream_data.max_stream_data = 8092;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving MAX_STREAM_DATA to a remote bidirectional stream which
+ exceeds limit */
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.max_stream_data.stream_id = 1;
+ fr.max_stream_data.max_stream_data = 1000000009;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving MAX_STREAM_DATA to a remote bidirectional stream which
+ the local endpoint has not received yet. */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.max_stream_data.stream_id = 4;
+ fr.max_stream_data.max_stream_data = 1000000009;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(NULL != strm);
+ CU_ASSERT(1000000009 == strm->tx.max_offset);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving MAX_STREAM_DATA to a idle remote unidirectional stream
+ is a protocol violation. */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.max_stream_data.stream_id = 2;
+ fr.max_stream_data.max_stream_data = 1000000009;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving MAX_STREAM_DATA to an existing bidirectional stream */
+ setup_default_server(&conn);
+
+ strm = open_stream(conn, 4);
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.max_stream_data.stream_id = 4;
+ fr.max_stream_data.max_stream_data = 1000000009;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1000000009 == strm->tx.max_offset);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_send_early_data(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_ssize spktlen;
+ ngtcp2_ssize datalen;
+ uint8_t buf[1024];
+ int64_t stream_id;
+ int rv;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_vec datav;
+
+ setup_early_client(&conn);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+ stream_id, null_data, 1024, ++t);
+
+ CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen);
+ CU_ASSERT(670 == datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* 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, NULL, buf, sizeof(buf), &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ 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, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+ stream_id, 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_writev_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ -1, NULL, 0, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ /* We have written Initial. Now check that STREAM frame is
+ written. */
+ spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, 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_writev_stream(
+ conn, NULL, NULL, buf,
+ NGTCP2_MIN_LONG_HEADERLEN + 1 + ngtcp2_conn_get_dcid(conn)->datalen +
+ conn->oscid.datalen + 300,
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id, NULL, 0, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(-1 == datalen);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_early_data(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_frame fr;
+ int64_t pkt_num = 1;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_strm *strm;
+ ngtcp2_cid rcid;
+ int rv;
+ my_user_data ud;
+
+ rcid_init(&rcid);
+
+ setup_early_server(&conn);
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1221;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 911;
+ fr.stream.data[0].base = null_data;
+
+ pktlen =
+ write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn),
+ ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(4 == ud.stream_data.stream_id);
+ CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_EARLY);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(NULL != strm);
+ CU_ASSERT(911 == strm->rx.last_offset);
+
+ ngtcp2_conn_del(conn);
+
+ /* Re-ordered 0-RTT packet */
+ setup_early_server(&conn);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 119;
+ fr.stream.data[0].base = null_data;
+
+ pktlen =
+ write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn),
+ ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_RETRY == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Compound packet */
+ setup_early_server(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 111;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 1;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 999;
+ fr.stream.data[0].base = null_data;
+
+ pktlen += write_0rtt_pkt(buf + pktlen, sizeof(buf) - pktlen, &rcid,
+ ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ strm = ngtcp2_conn_find_stream(conn, 4);
+
+ CU_ASSERT(NULL != strm);
+ CU_ASSERT(999 == strm->rx.last_offset);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_compound_pkt(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_frame fr;
+ int64_t pkt_num = 1;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_acktr_entry *ackent;
+ int rv;
+ ngtcp2_ksl_it it;
+
+ /* 2 QUIC long packets in one UDP packet */
+ setup_handshake_server(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 611;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ pktlen += write_initial_pkt(buf + pktlen, sizeof(buf) - pktlen, &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1,
+ &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_acktr_get(&conn->in_pktns->acktr);
+ ackent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(pkt_num == ackent->pkt_num);
+ CU_ASSERT(2 == ackent->len);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_conn_del(conn);
+
+ /* 1 long packet and 1 short packet in one UDP packet */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_PADDING;
+ fr.padding.len = 1;
+
+ pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 426;
+ fr.stream.data[0].base = null_data;
+
+ pktlen += write_pkt(buf + pktlen, sizeof(buf) - pktlen, &conn->oscid,
+ ++pkt_num, &fr, 1, conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_acktr_get(&conn->pktns.acktr);
+ ackent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(ackent->pkt_num == pkt_num);
+
+ it = ngtcp2_acktr_get(&conn->hs_pktns->acktr);
+
+ CU_ASSERT(!ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_pkt_payloadlen(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_frame fr;
+ int64_t pkt_num = 1;
+ ngtcp2_tstamp t = 0;
+ uint64_t payloadlen;
+ int rv;
+ const ngtcp2_cid *dcid;
+
+ /* Payload length is invalid */
+ setup_handshake_server(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1231;
+ fr.crypto.data[0].base = null_data;
+
+ dcid = ngtcp2_conn_get_dcid(conn);
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, dcid, ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1,
+ &null_ckm);
+
+ payloadlen = read_pkt_payloadlen(buf, dcid, &conn->oscid);
+ write_pkt_payloadlen(buf, dcid, &conn->oscid, payloadlen + 1);
+
+ /* This first packet which does not increase initial packet number
+ space CRYPTO offset or it does not get buffered as 0RTT is an
+ error. But it is unsecured Initial, so we just ignore it. */
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv);
+ CU_ASSERT(NGTCP2_CS_SERVER_INITIAL == conn->state);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client Initial packet included in UDP datagram smaller than 1200
+ is discarded. */
+ setup_handshake_server(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1000;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1,
+ NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv);
+ CU_ASSERT(NGTCP2_CS_SERVER_INITIAL == conn->state);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_writev_stream(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 0;
+ int rv;
+ int64_t stream_id;
+ ngtcp2_vec datav = {null_data, 10};
+ ngtcp2_ssize datalen;
+ size_t left;
+
+ /* 0 length STREAM should not be written if we supply nonzero length
+ data. */
+ setup_default_client(&conn);
+
+ /* This will sends NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ /*
+ * Long header (1+18+1)
+ * STREAM overhead (+3)
+ * AEAD overhead (16)
+ */
+ spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 39, &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ &datav, 1, ++t);
+
+ CU_ASSERT(0 == spktlen);
+ CU_ASSERT(-1 == datalen);
+
+ ngtcp2_conn_del(conn);
+
+ /* +1 buffer size */
+ setup_default_client(&conn);
+
+ /* This will sends NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 40, &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ &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, NULL, buf, 1200, &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
+ &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_WRITE_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, NULL, buf, 1200, &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
+ &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen);
+ CU_ASSERT(10 == datalen);
+ CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, 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, NULL, buf, 1200, &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
+ &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_WRITE_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, NULL, buf, 1200, &datalen,
+ NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
+ &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen);
+ CU_ASSERT(10 == datalen);
+ CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ /* Make sure that packet is padded */
+ CU_ASSERT(1200 == spktlen);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_writev_datagram(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_vec datav = {null_data, 10};
+ ngtcp2_vec vec;
+ int accepted;
+ my_user_data ud;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ int rv;
+
+ setup_default_client(&conn);
+ conn->callbacks.ack_datagram = ack_datagram;
+ conn->remote.transport_params->max_datagram_frame_size = 1 + 1 + 10;
+ conn->user_data = &ud;
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 1000000009, &datav, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 != accepted);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ ud.datagram.dgram_id = 0;
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1000000009 == ud.datagram.dgram_id);
+
+ ngtcp2_conn_del(conn);
+
+ /* Coalesces multiple DATAGRAM frames into a single QUIC packet */
+ setup_default_client(&conn);
+ conn->remote.transport_params->max_datagram_frame_size = 65535;
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_MORE, 1000000007, &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen);
+ CU_ASSERT(0 != accepted);
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_MORE, 1000000007, &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen);
+ CU_ASSERT(0 != accepted);
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 0, &datav, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 != accepted);
+
+ ngtcp2_conn_del(conn);
+
+ /* DATAGRAM cannot fit into QUIC packet because the other frames
+ occupy the space */
+ setup_default_client(&conn);
+ conn->remote.transport_params->max_datagram_frame_size =
+ 1 + ngtcp2_put_uvarintlen(2000) + 2000;
+
+ vec.base = null_data;
+ vec.len = 2000;
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 987, &vec, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 == accepted);
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 545, &vec, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 != accepted);
+
+ ngtcp2_conn_del(conn);
+
+ /* Calling ngtcp2_conn_writev_datagram without receiving positive
+ max_datagram_frame_size is an error */
+ setup_default_client(&conn);
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 999, &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_STATE == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Sending DATAGRAM which is larger than the value of received
+ max_datagram_frame_size is an error */
+ setup_default_client(&conn);
+ conn->remote.transport_params->max_datagram_frame_size = 9;
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 4433, &datav, 1, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Send DATAGRAM frame in a 0RTT packet */
+ setup_early_client(&conn);
+
+ conn->remote.transport_params->max_datagram_frame_size = 4311;
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 22360679, &datav, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 != accepted);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_datagram(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_frame fr;
+ size_t pktlen;
+ int64_t pkt_num = 0;
+ ngtcp2_tstamp t = 0;
+ my_user_data ud;
+ int rv;
+ ngtcp2_cid rcid;
+
+ rcid_init(&rcid);
+
+ setup_default_server(&conn);
+ conn->user_data = &ud;
+ conn->callbacks.recv_datagram = recv_datagram;
+ conn->local.transport_params.max_datagram_frame_size = 1 + 1111;
+
+ fr.type = NGTCP2_FRAME_DATAGRAM;
+ fr.datagram.data = fr.datagram.rdata;
+ fr.datagram.data->base = null_data;
+ fr.datagram.data->len = 1111;
+ fr.datagram.datacnt = 1;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1111 == ud.datagram.datalen);
+ CU_ASSERT(!(NGTCP2_DATAGRAM_FLAG_EARLY & ud.datagram.flags));
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving DATAGRAM frame which is strictly larger than the
+ declared limit is an error */
+ setup_default_server(&conn);
+ conn->local.transport_params.max_datagram_frame_size = 1 + 1111 - 1;
+
+ fr.type = NGTCP2_FRAME_DATAGRAM;
+ fr.datagram.data = fr.datagram.rdata;
+ fr.datagram.data->base = null_data;
+ fr.datagram.data->len = 1111;
+ fr.datagram.datacnt = 1;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_PROTO == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving DATAGRAM frame in a 0RTT packet */
+ setup_early_server(&conn);
+ conn->user_data = &ud;
+ conn->callbacks.recv_datagram = recv_datagram;
+ conn->local.transport_params.max_datagram_frame_size = 1 + 1111;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1199;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_DATAGRAM;
+ fr.datagram.data = fr.datagram.rdata;
+ fr.datagram.data->base = null_data;
+ fr.datagram.data->len = 1111;
+ fr.datagram.datacnt = 1;
+
+ pktlen =
+ write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn),
+ ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ memset(&ud, 0, sizeof(ud));
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1111 == ud.datagram.datalen);
+ CU_ASSERT(NGTCP2_DATAGRAM_FLAG_EARLY & ud.datagram.flags);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_new_connection_id(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 0;
+ int64_t pkt_num = 0;
+ ngtcp2_frame fr;
+ ngtcp2_frame frs[16];
+ const uint8_t cid[] = {0xf0, 0xf1, 0xf2, 0xf3};
+ const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff};
+ const uint8_t cid2[] = {0xf0, 0xf1, 0xf2, 0xf4};
+ const uint8_t token2[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xfe};
+ const uint8_t cid3[] = {0xf0, 0xf1, 0xf2, 0xf5};
+ const uint8_t token3[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xfd};
+ ngtcp2_dcid *dcid;
+ int rv;
+ ngtcp2_frame_chain *frc;
+ size_t i;
+
+ setup_default_client(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&fr.new_connection_id.cid, cid, sizeof(cid));
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+
+ assert(ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+
+ CU_ASSERT(ngtcp2_cid_eq(&fr.new_connection_id.cid, &dcid->cid));
+ CU_ASSERT(dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT);
+ CU_ASSERT(0 == memcmp(fr.new_connection_id.stateless_reset_token, dcid->token,
+ sizeof(fr.new_connection_id.stateless_reset_token)));
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 2;
+ fr.new_connection_id.retire_prior_to = 2;
+ ngtcp2_cid_init(&fr.new_connection_id.cid, cid2, sizeof(cid2));
+ memcpy(fr.new_connection_id.stateless_reset_token, token2, sizeof(token2));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb));
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(2 == conn->dcid.current.seq);
+ CU_ASSERT(NULL != conn->pktns.tx.frq);
+ CU_ASSERT(2 == conn->dcid.retire_prior_to);
+
+ frc = conn->pktns.tx.frq;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+
+ frc = frc->next;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(NULL == frc->next);
+
+ /* This will send RETIRE_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ ngtcp2_conn_del(conn);
+
+ /* Received connection ID is immediately retired due to packet
+ reordering */
+ setup_default_client(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 2;
+ fr.new_connection_id.retire_prior_to = 2;
+ ngtcp2_cid_init(&fr.new_connection_id.cid, cid, sizeof(cid));
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(2 == conn->dcid.current.seq);
+ CU_ASSERT(2 == conn->dcid.retire_prior_to);
+
+ frc = conn->pktns.tx.frq;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(NULL == frc->next);
+
+ /* This will send RETIRE_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&fr.new_connection_id.cid, cid2, sizeof(cid2));
+ memcpy(fr.new_connection_id.stateless_reset_token, token2, sizeof(token2));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(2 == conn->dcid.current.seq);
+ CU_ASSERT(2 == conn->dcid.retire_prior_to);
+
+ frc = conn->pktns.tx.frq;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(NULL == frc->next);
+
+ ngtcp2_conn_del(conn);
+
+ /* ngtcp2_pv contains DCIDs that should be retired. */
+ setup_default_server(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ assert(NULL == conn->pv);
+
+ frs[0].type = NGTCP2_FRAME_PING;
+ frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[1].new_connection_id.seq = 1;
+ frs[1].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid));
+ memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token));
+ frs[2].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[2].new_connection_id.seq = 2;
+ frs[2].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[2].new_connection_id.cid, cid2, sizeof(cid2));
+ memcpy(frs[2].new_connection_id.stateless_reset_token, token2,
+ sizeof(token2));
+ frs[3].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[3].new_connection_id.seq = 3;
+ frs[3].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[3].new_connection_id.cid, cid3, sizeof(cid3));
+ memcpy(frs[3].new_connection_id.stateless_reset_token, token3,
+ sizeof(token3));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 4,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ assert(NULL != conn->pv);
+
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE);
+ CU_ASSERT(1 == conn->pv->dcid.seq);
+ CU_ASSERT(0 == conn->pv->fallback_dcid.seq);
+ CU_ASSERT(2 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 3;
+ fr.new_connection_id.retire_prior_to = 2;
+ ngtcp2_cid_init(&fr.new_connection_id.cid, cid3, sizeof(cid3));
+ memcpy(fr.new_connection_id.stateless_reset_token, token3, sizeof(token3));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE);
+ CU_ASSERT(2 == conn->pv->dcid.seq);
+ CU_ASSERT(3 == conn->pv->fallback_dcid.seq);
+
+ frc = conn->pktns.tx.frq;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(0 == frc->fr.retire_connection_id.seq);
+ frc = frc->next;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(1 == frc->fr.retire_connection_id.seq);
+ CU_ASSERT(NULL == frc->next);
+
+ ngtcp2_conn_del(conn);
+
+ /* ngtcp2_pv contains DCID in fallback that should be retired and
+ there is not enough connection ID left. */
+ setup_default_server(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ assert(NULL == conn->pv);
+
+ frs[0].type = NGTCP2_FRAME_PING;
+ frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[1].new_connection_id.seq = 1;
+ frs[1].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid));
+ memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ assert(NULL != conn->pv);
+
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE);
+ CU_ASSERT(1 == conn->pv->dcid.seq);
+ CU_ASSERT(0 == conn->pv->fallback_dcid.seq);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 2;
+ fr.new_connection_id.retire_prior_to = 2;
+ ngtcp2_cid_init(&fr.new_connection_id.cid, cid2, sizeof(cid2));
+ memcpy(fr.new_connection_id.stateless_reset_token, token2, sizeof(token2));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2 == conn->dcid.current.seq);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(NULL == conn->pv);
+
+ frc = conn->pktns.tx.frq;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(0 == frc->fr.retire_connection_id.seq);
+
+ frc = frc->next;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(1 == frc->fr.retire_connection_id.seq);
+ CU_ASSERT(NULL == frc->next);
+
+ ngtcp2_conn_del(conn);
+
+ /* ngtcp2_pv contains DCIDs that should be retired and there is not
+ enough connection ID left to continue path validation. */
+ setup_default_server(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ assert(NULL == conn->pv);
+
+ frs[0].type = NGTCP2_FRAME_PING;
+ frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[1].new_connection_id.seq = 1;
+ frs[1].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid));
+ memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ assert(NULL != conn->pv);
+
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE);
+ CU_ASSERT(1 == conn->pv->dcid.seq);
+ CU_ASSERT(0 == conn->pv->fallback_dcid.seq);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+
+ /* Overwrite seq in pv->dcid so that pv->dcid cannot be renewed. */
+ conn->pv->dcid.seq = 2;
+ /* Internally we assume that if primary dcid and pv->dcid differ,
+ then no fallback dcid is present. */
+ conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE;
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 3;
+ fr.new_connection_id.retire_prior_to = 3;
+ ngtcp2_cid_init(&fr.new_connection_id.cid, cid3, sizeof(cid3));
+ memcpy(fr.new_connection_id.stateless_reset_token, token3, sizeof(token3));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(3 == conn->dcid.current.seq);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(NULL == conn->pv);
+
+ frc = conn->pktns.tx.frq;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(2 == frc->fr.retire_connection_id.seq);
+
+ frc = frc->next;
+
+ CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type);
+ CU_ASSERT(1 == frc->fr.retire_connection_id.seq);
+ CU_ASSERT(NULL == frc->next);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving more than advertised CID is treated as error */
+ setup_default_server(&conn);
+ conn->local.transport_params.active_connection_id_limit = 2;
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ assert(NULL == conn->pv);
+
+ frs[0].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[0].new_connection_id.seq = 1;
+ frs[0].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[0].new_connection_id.cid, cid, sizeof(cid));
+ memcpy(frs[0].new_connection_id.stateless_reset_token, token, sizeof(token));
+ frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[1].new_connection_id.seq = 2;
+ frs[1].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid2, sizeof(cid2));
+ memcpy(frs[1].new_connection_id.stateless_reset_token, token2,
+ sizeof(token2));
+ frs[2].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[2].new_connection_id.seq = 3;
+ frs[2].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[2].new_connection_id.cid, cid3, sizeof(cid3));
+ memcpy(frs[2].new_connection_id.stateless_reset_token, token3,
+ sizeof(token3));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 3,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_CONNECTION_ID_LIMIT == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving duplicated NEW_CONNECTION_ID frame */
+ setup_default_server(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ frs[0].type = NGTCP2_FRAME_PING;
+
+ frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[1].new_connection_id.seq = 1;
+ frs[1].new_connection_id.retire_prior_to = 1;
+ ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid));
+ memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ frs[2].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[2].new_connection_id.seq = 2;
+ frs[2].new_connection_id.retire_prior_to = 1;
+ ngtcp2_cid_init(&frs[2].new_connection_id.cid, cid2, sizeof(cid2));
+ memcpy(frs[2].new_connection_id.stateless_reset_token, token2,
+ sizeof(token2));
+
+ frs[3].type = NGTCP2_FRAME_PADDING;
+ frs[3].padding.len = 1200;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 4,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(2 == conn->dcid.current.seq);
+ CU_ASSERT(NULL != conn->pv);
+ CU_ASSERT(ngtcp2_cid_eq(&frs[1].new_connection_id.cid,
+ &conn->pv->fallback_dcid.cid));
+
+ /* This will send PATH_CHALLENGE frame */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen >= 1200);
+
+ fr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ memset(fr.path_response.data, 0, sizeof(fr.path_response.data));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ /* Server starts probing old path */
+ CU_ASSERT(NULL != conn->pv);
+ CU_ASSERT(ngtcp2_path_eq(&null_path.path, &conn->pv->dcid.ps.path));
+
+ /* Receive NEW_CONNECTION_ID seq=1 again, which should be ignored. */
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb));
+ CU_ASSERT(2 == conn->dcid.current.seq);
+
+ ngtcp2_conn_del(conn);
+
+ /* Exceeding the limit for the number of unacknowledged
+ RETIRE_CONNECTION_ID leads to NGTCP2_ERR_CONNECTION_ID_LIMIT. */
+ setup_default_server(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ for (i = 0; i < 7; ++i) {
+ frs[i].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[i].new_connection_id.seq = i + 1;
+ frs[i].new_connection_id.retire_prior_to = 0;
+ ngtcp2_cid_init(&frs[i].new_connection_id.cid, cid, sizeof(cid));
+ frs[i].new_connection_id.cid.data[0] = (uint8_t)i;
+ memcpy(frs[i].new_connection_id.stateless_reset_token, token,
+ sizeof(token));
+ }
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 7,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < 8; ++i) {
+ frs[i].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[i].new_connection_id.seq = i + 8;
+ frs[i].new_connection_id.retire_prior_to = 8;
+ ngtcp2_cid_init(&frs[i].new_connection_id.cid, cid, sizeof(cid));
+ frs[i].new_connection_id.cid.data[0] = (uint8_t)(i + 8);
+ memcpy(frs[i].new_connection_id.stateless_reset_token, token,
+ sizeof(token));
+ }
+
+ for (i = 0; i < 8; ++i) {
+ frs[i + 8].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[i + 8].new_connection_id.seq = i + 16;
+ frs[i + 8].new_connection_id.retire_prior_to = 16;
+ ngtcp2_cid_init(&frs[i + 8].new_connection_id.cid, cid, sizeof(cid));
+ frs[i + 8].new_connection_id.cid.data[0] = (uint8_t)(i + 16);
+ memcpy(frs[i + 8].new_connection_id.stateless_reset_token, token,
+ sizeof(token));
+ }
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 16,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ frs[0].new_connection_id.seq = 24;
+ frs[0].new_connection_id.retire_prior_to = 17;
+ ngtcp2_cid_init(&frs[0].new_connection_id.cid, cid, sizeof(cid));
+ frs[0].new_connection_id.cid.data[0] = (uint8_t)(i + 24);
+ memcpy(frs[0].new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_CONNECTION_ID_LIMIT == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_retire_connection_id(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 1000000009;
+ int64_t pkt_num = 0;
+ ngtcp2_frame fr;
+ int rv;
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+ uint64_t seq;
+
+ setup_default_client(&conn);
+ conn->remote.transport_params->active_connection_id_limit = 7;
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t);
+
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_ksl_begin(&conn->scid.set);
+ scid = ngtcp2_ksl_it_get(&it);
+ seq = scid->seq;
+
+ CU_ASSERT(NGTCP2_SCID_FLAG_NONE == scid->flags);
+ CU_ASSERT(UINT64_MAX == scid->retired_ts);
+ CU_ASSERT(1 == ngtcp2_pq_size(&conn->scid.used));
+
+ fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+ fr.retire_connection_id.seq = seq;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_SCID_FLAG_RETIRED == scid->flags);
+ CU_ASSERT(1000000010 == scid->retired_ts);
+ CU_ASSERT(2 == ngtcp2_pq_size(&conn->scid.used));
+ CU_ASSERT(7 == ngtcp2_ksl_len(&conn->scid.set));
+ CU_ASSERT(1 == conn->scid.num_retired);
+
+ /* One NEW_CONNECTION_ID frame is sent as a replacement. */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(8 == ngtcp2_ksl_len(&conn->scid.set));
+ CU_ASSERT(1 == conn->scid.num_retired);
+
+ /* No NEW_CONNECTION_ID frames should be sent. */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen == 0);
+ CU_ASSERT(8 == ngtcp2_ksl_len(&conn->scid.set));
+ CU_ASSERT(1 == conn->scid.num_retired);
+
+ /* Now time passed and retired connection ID is removed */
+ t += 7 * NGTCP2_DEFAULT_INITIAL_RTT;
+
+ ngtcp2_conn_handle_expiry(conn, t);
+
+ CU_ASSERT(7 == ngtcp2_ksl_len(&conn->scid.set));
+ CU_ASSERT(0 == conn->scid.num_retired);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving RETIRE_CONNECTION_ID with seq which is greater than the
+ sequence number previously sent must be treated as error */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+ fr.retire_connection_id.seq = 1;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_PROTO == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_server_path_validation(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 900;
+ int64_t pkt_num = 0;
+ ngtcp2_frame fr;
+ int rv;
+ const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00};
+ ngtcp2_cid cid, *new_cid;
+ const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff};
+ ngtcp2_path_storage new_path1, new_path2;
+ ngtcp2_ksl_it it;
+
+ path_init(&new_path1, 0, 0, 2, 0);
+ path_init(&new_path2, 0, 0, 3, 0);
+
+ ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid));
+
+ setup_default_server(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(ngtcp2_ksl_len(&conn->scid.set) > 1);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_PING;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->pv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->pv->ents.rb) > 0);
+
+ fr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ memset(fr.path_response.data, 0, sizeof(fr.path_response.data));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_path_eq(&new_path1.path, &conn->dcid.current.ps.path));
+ /* DCID does not change because the client does not change its
+ DCID. */
+ CU_ASSERT(!ngtcp2_cid_eq(&cid, &conn->dcid.current.cid));
+
+ /* A remote endpoint changes DCID as well */
+ fr.type = NGTCP2_FRAME_PING;
+
+ it = ngtcp2_ksl_begin(&conn->scid.set);
+
+ assert(!ngtcp2_ksl_it_end(&it));
+
+ new_cid = &(((ngtcp2_scid *)ngtcp2_ksl_it_get(&it))->cid);
+
+ pktlen = write_pkt(buf, sizeof(buf), new_cid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->pv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->pv->ents.rb) > 0);
+
+ fr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ memset(fr.path_response.data, 0, sizeof(fr.path_response.data));
+
+ pktlen = write_pkt(buf, sizeof(buf), new_cid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_path_eq(&new_path2.path, &conn->dcid.current.ps.path));
+ CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->dcid.current.cid));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_client_connection_migration(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_tstamp t = 900;
+ int64_t pkt_num = 0;
+ ngtcp2_frame fr;
+ int rv;
+ const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00};
+ ngtcp2_cid cid;
+ const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff};
+ my_user_data ud;
+ ngtcp2_ssize spktlen;
+ ngtcp2_path_storage to_path;
+
+ ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid));
+
+ /* immediate migration */
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_path_storage_init2(&to_path, &new_path.path);
+ to_path.path.user_data = &ud;
+
+ rv = ngtcp2_conn_initiate_immediate_migration(conn, &to_path.path, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->pv);
+ CU_ASSERT(ngtcp2_path_eq(&to_path.path, &conn->dcid.current.ps.path));
+ CU_ASSERT(&ud == conn->dcid.current.ps.path.user_data);
+ CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->dcid.current.cid));
+ CU_ASSERT(ngtcp2_path_eq(&to_path.path, &conn->pv->dcid.ps.path));
+ CU_ASSERT(&ud == conn->pv->dcid.ps.path.user_data);
+ CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->pv->dcid.cid));
+
+ ngtcp2_conn_del(conn);
+
+ /* migrate after successful path validation */
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_path_storage_init2(&to_path, &new_path.path);
+ to_path.path.user_data = &ud;
+
+ rv = ngtcp2_conn_initiate_migration(conn, &to_path.path, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->pv);
+ CU_ASSERT(ngtcp2_path_eq(&null_path.path, &conn->dcid.current.ps.path));
+ CU_ASSERT(NULL == conn->dcid.current.ps.path.user_data);
+ CU_ASSERT(ngtcp2_cid_eq(&conn->rcid, &conn->dcid.current.cid));
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 < spktlen);
+
+ fr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ memset(fr.path_response.data, 0, sizeof(fr.path_response.data));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == conn->pv);
+ CU_ASSERT(ngtcp2_path_eq(&to_path.path, &conn->dcid.current.ps.path));
+ CU_ASSERT(&ud == conn->dcid.current.ps.path.user_data);
+ CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->dcid.current.cid));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_path_challenge(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 11;
+ int64_t pkt_num = 0;
+ ngtcp2_frame fr;
+ ngtcp2_frame frs[2];
+ int rv;
+ const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00};
+ ngtcp2_cid cid;
+ const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff};
+ const uint8_t data[] = {0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8};
+ const uint8_t data2[] = {0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf9};
+ ngtcp2_path_storage ps;
+ ngtcp2_ssize shdlen;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_dcid *dcid;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+
+ ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid));
+
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE;
+ memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data));
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1200;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0);
+
+ ngtcp2_path_storage_zero(&ps);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen >= 1200);
+ CU_ASSERT(ngtcp2_path_eq(&new_path.path, &ps.path));
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb));
+ CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb));
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0);
+
+ CU_ASSERT((uint64_t)spktlen == dcid->bytes_sent);
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(ngtcp2_cid_eq(&cid, &hd.dcid));
+
+ /* Use same bound DCID for PATH_CHALLENGE from the same path. */
+ fr.type = NGTCP2_FRAME_PATH_CHALLENGE;
+ memcpy(fr.path_challenge.data, data2, sizeof(fr.path_challenge.data));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0);
+
+ ngtcp2_path_storage_zero(&ps);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(ngtcp2_path_eq(&new_path.path, &ps.path));
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb));
+ CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb));
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(ngtcp2_cid_eq(&cid, &hd.dcid));
+
+ ngtcp2_conn_del(conn);
+
+ /* PATH_CHALLENGE from the current path */
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE;
+ memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data));
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1200;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0);
+
+ ngtcp2_path_storage_zero(&ps);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen >= 1200);
+ CU_ASSERT(ngtcp2_path_eq(&null_path.path, &ps.path));
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb));
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb));
+ CU_ASSERT((uint64_t)spktlen == conn->dcid.current.bytes_sent);
+
+ ngtcp2_conn_del(conn);
+
+ /* PATH_CHALLENGE should be ignored with server
+ disable_active_migration */
+ setup_default_server(&conn);
+
+ conn->local.transport_params.disable_active_migration = 1;
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE;
+ memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data));
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1200;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb));
+
+ ngtcp2_conn_del(conn);
+
+ /* PATH_CHALLENGE on NAT rebinding (passive migration) should be
+ accepted with server disable_active_migration */
+ setup_default_server(&conn);
+
+ conn->local.transport_params.disable_active_migration = 1;
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE;
+ memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data));
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1200;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_nat_path.path, &null_pi, buf, pktlen,
+ ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0);
+
+ ngtcp2_conn_del(conn);
+
+ /* PATH_CHALLENGE to preferred address should be accepted with
+ server disable_active_migration */
+ server_default_transport_params(&params);
+ params.disable_active_migration = 1;
+ params.preferred_address_present = 1;
+ params.preferred_address.cid = cid;
+
+ /* Set local address of new_path */
+ assert(AF_INET == new_path.path.local.addr->sa_family);
+
+ params.preferred_address.ipv4_present = 1;
+ memcpy(&params.preferred_address.ipv4, new_path.path.local.addr,
+ sizeof(params.preferred_address.ipv4));
+
+ server_default_settings(&settings);
+
+ setup_default_server_settings(&conn, &null_path.path, &settings, &params);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE;
+ memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data));
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1200;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_key_update(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 19393;
+ int64_t pkt_num = -1;
+ ngtcp2_frame fr;
+ int rv;
+ int64_t stream_id;
+ ngtcp2_ssize nwrite;
+
+ setup_default_server(&conn);
+
+ /* The remote endpoint initiates key update */
+ fr.type = NGTCP2_FRAME_PING;
+
+ pktlen =
+ write_pkt_flags(buf, sizeof(buf), NGTCP2_PKT_FLAG_KEY_PHASE, &conn->oscid,
+ ++pkt_num, &fr, 1, conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->crypto.key_update.old_rx_ckm);
+ CU_ASSERT(NULL == conn->crypto.key_update.new_tx_ckm);
+ CU_ASSERT(NULL == conn->crypto.key_update.new_rx_ckm);
+ CU_ASSERT(UINT64_MAX == conn->crypto.key_update.confirmed_ts);
+ CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED);
+ CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR));
+
+ t += NGTCP2_SECONDS;
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(t == conn->crypto.key_update.confirmed_ts);
+ CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED));
+ CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR));
+
+ t += ngtcp2_conn_get_pto(conn) + 1;
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t);
+
+ CU_ASSERT(0 == spktlen);
+ CU_ASSERT(NULL == conn->crypto.key_update.old_rx_ckm);
+ CU_ASSERT(NULL != conn->crypto.key_update.new_tx_ckm);
+ CU_ASSERT(NULL != conn->crypto.key_update.new_rx_ckm);
+
+ /* The local endpoint initiates key update */
+ t += ngtcp2_conn_get_pto(conn) * 2;
+
+ rv = ngtcp2_conn_initiate_key_update(conn, t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->crypto.key_update.old_rx_ckm);
+ CU_ASSERT(NULL == conn->crypto.key_update.new_tx_ckm);
+ CU_ASSERT(NULL == conn->crypto.key_update.new_rx_ckm);
+ CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED);
+ CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR);
+
+ rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 1024, ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen =
+ write_pkt_flags(buf, sizeof(buf), NGTCP2_PKT_FLAG_KEY_PHASE, &conn->oscid,
+ ++pkt_num, &fr, 1, conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(t == conn->crypto.key_update.confirmed_ts);
+ CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED));
+ CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_crypto_buffer_exceeded(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_tstamp t = 11111;
+ int64_t pkt_num = -1;
+ ngtcp2_frame fr;
+ int rv;
+
+ setup_default_client(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 1000000;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].base = null_data;
+ fr.crypto.data[0].len = 1;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_handshake_probe(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_ssize spktlen;
+ size_t pktlen;
+ uint8_t buf[1200];
+ ngtcp2_frame fr;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ksl_it it;
+ int rv;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ /* Retransmit first Initial on PTO timer */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
+
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(1 == conn->in_pktns->rtb.num_retransmittable);
+ CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting);
+ CU_ASSERT(0 == conn->in_pktns->rtb.probe_pkt_left);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1,
+ NULL, 0, &fr, 1, &null_ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
+
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == conn->in_pktns->rtb.num_retransmittable);
+ CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
+ CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left);
+
+ /* This sends anti-deadlock padded Initial packet even if we have
+ nothing to send. */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 == conn->in_pktns->rtb.num_retransmittable);
+ CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting);
+ CU_ASSERT(0 == conn->in_pktns->rtb.probe_pkt_left);
+
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE);
+ CU_ASSERT(sizeof(buf) == ent->pktlen);
+
+ init_crypto_ctx(&crypto_ctx);
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting);
+ CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ /* This sends anti-deadlock Handshake packet even if we have nothing
+ to send. */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(0 == conn->hs_pktns->rtb.num_retransmittable);
+ CU_ASSERT(1 == conn->hs_pktns->rtb.num_ack_eliciting);
+ CU_ASSERT(0 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ it = ngtcp2_rtb_head(&conn->hs_pktns->rtb);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE);
+ CU_ASSERT(sizeof(buf) > ent->pktlen);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_handshake_loss(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_ssize spktlen;
+ size_t i;
+ size_t pktlen;
+ uint8_t buf[1252];
+ ngtcp2_frame fr;
+ ngtcp2_frame frs[2];
+ ngtcp2_cid rcid;
+ int rv;
+ int64_t pkt_num = -1;
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+
+ rcid_init(&rcid);
+ setup_handshake_server(&conn);
+ conn->callbacks.recv_crypto_data = recv_crypto_data;
+
+ frs[0].type = NGTCP2_FRAME_CRYPTO;
+ frs[0].crypto.offset = 0;
+ frs[0].crypto.datacnt = 1;
+ frs[0].crypto.data[0].len = 123;
+ frs[0].crypto.data[0].base = null_data;
+
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1005;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, frs, 2, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ /* Increase anti-amplification factor for easier testing */
+ conn->dcid.current.bytes_recv += 10000;
+
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data,
+ 123);
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data,
+ 163);
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data,
+ 2369);
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data,
+ 79);
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data,
+ 36);
+
+ /* Initial and first Handshake are coalesced into 1 packet. */
+ for (i = 0; i < 3; ++i) {
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+ CU_ASSERT(spktlen > 0);
+ }
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ t += 30 * NGTCP2_MILLISECONDS;
+
+ ngtcp2_conn_on_loss_detection_timer(conn, t);
+
+ CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left);
+ CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ /* Send a PTO probe packet */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(0 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(987 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(3 == ent->hd.pkt_num);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = 2;
+ fr.ack.ack_delay = 0;
+ fr.ack.ack_delay_unscaled = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t);
+
+ CU_ASSERT(0 == rv);
+
+ t += 40 * NGTCP2_MILLISECONDS;
+
+ ngtcp2_conn_on_loss_detection_timer(conn, t);
+
+ CU_ASSERT(0 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ /* Retransmits the contents of lost packet */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type);
+ CU_ASSERT(987 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(1 == ent->frc->fr.crypto.datacnt);
+ CU_ASSERT(1183 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(4 == ent->hd.pkt_num);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ t += 30 * NGTCP2_MILLISECONDS;
+
+ ngtcp2_conn_on_loss_detection_timer(conn, t);
+
+ CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ /* Send a PTO probe packet */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type);
+ CU_ASSERT(0 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(2 == ent->frc->fr.crypto.datacnt);
+ CU_ASSERT(987 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(5 == ent->hd.pkt_num);
+
+ ngtcp2_conn_del(conn);
+
+ /* Retransmission splits CRYPTO frame */
+ setup_handshake_server(&conn);
+ conn->callbacks.recv_crypto_data = recv_crypto_data;
+
+ frs[0].type = NGTCP2_FRAME_CRYPTO;
+ frs[0].crypto.offset = 0;
+ frs[0].crypto.datacnt = 1;
+ frs[0].crypto.data[0].len = 123;
+ frs[0].crypto.data[0].base = null_data;
+
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1005;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, frs, 2, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ /* Increase anti-amplification factor for easier testing */
+ conn->dcid.current.bytes_recv += 10000;
+
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data,
+ 123);
+ ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data,
+ 3000);
+ /* Initial and first Handshake are coalesced into 1 packet. */
+ for (i = 0; i < 3; ++i) {
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+ CU_ASSERT(spktlen > 0);
+ }
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type);
+ CU_ASSERT(2170 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(830 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(2 == ent->hd.pkt_num);
+
+ t += 30 * NGTCP2_MILLISECONDS;
+
+ ngtcp2_conn_on_loss_detection_timer(conn, t);
+
+ CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left);
+ CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ /* 1st PTO */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+ CU_ASSERT(spktlen > 0);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type);
+ CU_ASSERT(0 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(987 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(3 == ent->hd.pkt_num);
+
+ t += 30 * NGTCP2_MILLISECONDS;
+
+ ngtcp2_conn_on_loss_detection_timer(conn, t);
+
+ CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left);
+ CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ /* 2nd PTO. Initial and Handshake packets are coalesced. Handshake
+ CRYPTO is split into 2 because of Initial CRYPTO. */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type);
+ CU_ASSERT(987 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(991 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(4 == ent->hd.pkt_num);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type);
+ CU_ASSERT(1978 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(192 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(5 == ent->hd.pkt_num);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+ CU_ASSERT(0 == spktlen);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.ack_delay_unscaled = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ t += NGTCP2_MILLISECONDS;
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t);
+
+ CU_ASSERT(0 == rv);
+
+ t += 40 * NGTCP2_MILLISECONDS;
+
+ ngtcp2_conn_on_loss_detection_timer(conn, t);
+
+ CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
+
+ /* 3rd PTO */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+ CU_ASSERT(0 == spktlen);
+
+ it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type);
+ CU_ASSERT(2170 == ent->frc->fr.crypto.offset);
+ CU_ASSERT(830 == ngtcp2_vec_len(ent->frc->fr.crypto.data,
+ ent->frc->fr.crypto.datacnt));
+ CU_ASSERT(6 == ent->hd.pkt_num);
+ CU_ASSERT(NULL == ent->frc->next);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_client_initial_retry(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_frame fr;
+ int64_t pkt_num = -1;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_cid rcid;
+ int rv;
+
+ rcid_init(&rcid);
+
+ setup_handshake_server(&conn);
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 1;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1245;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_RETRY == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_client_initial_token(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_frame fr;
+ int64_t pkt_num = -1;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_cid rcid;
+ int rv;
+ const uint8_t raw_token[] = {0xff, 0x12, 0x31, 0x04, 0xab};
+ ngtcp2_vec token;
+ const ngtcp2_mem *mem;
+
+ rcid_init(&rcid);
+
+ setup_handshake_server(&conn);
+ mem = conn->mem;
+
+ token.base = ngtcp2_mem_malloc(mem, sizeof(raw_token));
+ memcpy(token.base, raw_token, sizeof(raw_token));
+ token.len = sizeof(raw_token);
+
+ conn->local.settings.token = token;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1181;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen =
+ write_initial_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn),
+ ++pkt_num, conn->client_chosen_version, raw_token,
+ sizeof(raw_token), &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1181 == ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm));
+
+ ngtcp2_conn_del(conn);
+
+ /* Specifying invalid token lets server drop the packet */
+ setup_handshake_server(&conn);
+ mem = conn->mem;
+
+ token.base = ngtcp2_mem_malloc(mem, sizeof(raw_token));
+ memcpy(token.base, raw_token, sizeof(raw_token));
+ token.len = sizeof(raw_token) - 1;
+
+ conn->local.settings.token = token;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1179;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen =
+ write_initial_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn),
+ ++pkt_num, conn->client_chosen_version, raw_token,
+ sizeof(raw_token), &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv);
+ CU_ASSERT(0 == ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_get_active_dcid(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_cid_token cid_token[2];
+ ngtcp2_cid dcid;
+ static uint8_t token[] = {0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1};
+
+ dcid_init(&dcid);
+ setup_default_client(&conn);
+
+ CU_ASSERT(1 == ngtcp2_conn_get_num_active_dcid(conn));
+ CU_ASSERT(1 == ngtcp2_conn_get_active_dcid(conn, cid_token));
+ CU_ASSERT(0 == cid_token[0].seq);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &cid_token[0].cid));
+ CU_ASSERT(ngtcp2_path_eq(&null_path.path, &cid_token[0].ps.path));
+ CU_ASSERT(1 == cid_token[0].token_present);
+ CU_ASSERT(0 ==
+ memcmp(token, cid_token[0].token, NGTCP2_STATELESS_RESET_TOKENLEN));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_recv_version_negotiation(void) {
+ ngtcp2_conn *conn;
+ const ngtcp2_cid *dcid;
+ ngtcp2_ssize spktlen;
+ uint8_t buf[1500];
+ uint32_t nsv[3];
+ int rv;
+ ngtcp2_tstamp t = 0;
+
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ dcid = ngtcp2_conn_get_dcid(conn);
+
+ nsv[0] = 0xffffffff;
+
+ spktlen = ngtcp2_pkt_write_version_negotiation(
+ buf, sizeof(buf), 0xfe, conn->oscid.data, conn->oscid.datalen, dcid->data,
+ dcid->datalen, nsv, 1);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_RECV_VERSION_NEGOTIATION == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Ignore Version Negotiation if it contains version selected by
+ client */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ dcid = ngtcp2_conn_get_dcid(conn);
+
+ nsv[0] = 0xfffffff0;
+ nsv[1] = conn->client_chosen_version;
+
+ spktlen = ngtcp2_pkt_write_version_negotiation(
+ buf, sizeof(buf), 0x50, conn->oscid.data, conn->oscid.datalen, dcid->data,
+ dcid->datalen, nsv, 2);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* Ignore Version Negotiation if client reacted upon Version
+ Negotiation */
+ setup_handshake_client(&conn);
+
+ conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT;
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ dcid = ngtcp2_conn_get_dcid(conn);
+
+ nsv[0] = 0xffffffff;
+
+ spktlen = ngtcp2_pkt_write_version_negotiation(
+ buf, sizeof(buf), 0xfe, conn->oscid.data, conn->oscid.datalen, dcid->data,
+ dcid->datalen, nsv, 1);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ (size_t)spktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_send_initial_token(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_callbacks cb;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ ngtcp2_cid rcid, scid;
+ ngtcp2_crypto_aead retry_aead = {0, NGTCP2_FAKE_AEAD_OVERHEAD};
+ uint8_t token[] = "this is token";
+ ngtcp2_ssize spktlen, shdlen;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+
+ rcid_init(&rcid);
+ scid_init(&scid);
+
+ init_initial_crypto_ctx(&crypto_ctx);
+
+ client_default_callbacks(&cb);
+ client_default_settings(&settings);
+ client_default_transport_params(&params);
+
+ settings.token.base = token;
+ settings.token.len = sizeof(token);
+
+ ngtcp2_conn_client_new(&conn, &rcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx,
+ null_iv, &hp_ctx, sizeof(null_iv));
+ ngtcp2_conn_set_retry_aead(conn, &retry_aead, &aead_ctx);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(sizeof(token) == hd.token.len);
+ CU_ASSERT(0 == memcmp(token, hd.token.base, sizeof(token)));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_set_remote_transport_params(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_transport_params params;
+ int rv;
+ ngtcp2_cid dcid;
+ uint8_t other_versions[2 * sizeof(uint32_t)];
+
+ dcid_init(&dcid);
+
+ /* client: Successful case */
+ setup_handshake_client(&conn);
+
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: Wrong original_dcid */
+ setup_handshake_client(&conn);
+
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: Wrong initial_scid */
+ setup_handshake_client(&conn);
+
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.original_dcid = conn->rcid;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: Receiving retry_scid when retry is not attempted */
+ setup_handshake_client(&conn);
+
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+ params.retry_scid_present = 1;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: Receiving retry_scid */
+ setup_handshake_client(&conn);
+
+ conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY;
+ conn->retry_scid = dcid;
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+ params.retry_scid_present = 1;
+ params.retry_scid = dcid;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: Not receiving retry_scid when retry is attempted */
+ setup_handshake_client(&conn);
+
+ conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY;
+ conn->retry_scid = dcid;
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: Special handling for QUIC v1 regarding Version
+ Negotiation */
+ setup_handshake_client(&conn);
+
+ conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT;
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: No version_information after Version Negotiation */
+ setup_handshake_client_version(&conn, NGTCP2_PROTO_VER_V2_DRAFT);
+
+ conn->local.settings.original_version = NGTCP2_PROTO_VER_V1;
+ conn->negotiated_version = conn->client_chosen_version;
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: other_versions includes the version that the client
+ initially attempted. */
+ setup_handshake_client(&conn);
+
+ conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT;
+ conn->negotiated_version = conn->client_chosen_version;
+
+ ngtcp2_put_uint32be(other_versions, NGTCP2_PROTO_VER_V1);
+ ngtcp2_put_uint32be(other_versions + sizeof(uint32_t),
+ NGTCP2_PROTO_VER_V2_DRAFT);
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+ params.version_info_present = 1;
+ params.version_info.chosen_version = conn->negotiated_version;
+ params.version_info.other_versions = other_versions;
+ params.version_info.other_versionslen = 2 * sizeof(uint32_t);
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: client is unable to choose client chosen version from
+ server's other_versions and chosen version. */
+ setup_handshake_client(&conn);
+
+ conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT;
+ conn->negotiated_version = 0xff000000u;
+
+ conn->local.settings.preferred_versions[0] = 0xff000001u;
+ conn->local.settings.preferred_versionslen = 1;
+
+ ngtcp2_put_uint32be(conn->vneg.other_versions, NGTCP2_PROTO_VER_V1);
+ ngtcp2_put_uint32be(conn->vneg.other_versions + sizeof(uint32_t),
+ 0xff000000u);
+
+ ngtcp2_put_uint32be(other_versions, 0xff000000u);
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+ params.version_info_present = 1;
+ params.version_info.chosen_version = conn->negotiated_version;
+ params.version_info.other_versions = other_versions;
+ params.version_info.other_versionslen = 1;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client: client chooses version which differs from client chosen
+ version from server's other_versions and chosen version. */
+ setup_handshake_client(&conn);
+
+ conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT;
+ conn->negotiated_version = 0xff000000u;
+
+ conn->local.settings.preferred_versions[0] = 0xff000000u;
+ conn->local.settings.preferred_versionslen = 1;
+
+ ngtcp2_put_uint32be(conn->vneg.other_versions, NGTCP2_PROTO_VER_V1);
+ ngtcp2_put_uint32be(conn->vneg.other_versions + sizeof(uint32_t),
+ 0xff000000u);
+
+ ngtcp2_put_uint32be(other_versions, 0xff000000u);
+
+ memset(&params, 0, sizeof(params));
+ params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params.max_udp_payload_size = 1450;
+ params.initial_scid = conn->dcid.current.cid;
+ params.original_dcid = conn->rcid;
+ params.version_info_present = 1;
+ params.version_info.chosen_version = conn->negotiated_version;
+ params.version_info.other_versions = other_versions;
+ params.version_info.other_versionslen = 1;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_write_connection_close(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[1200];
+ ngtcp2_ssize spktlen, shdlen;
+ ngtcp2_pkt_hd hd;
+ const uint8_t *p;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_crypto_ctx crypto_ctx;
+ ngtcp2_connection_close_error ccerr;
+
+ /* Client only Initial key */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
+
+ CU_ASSERT(spktlen > 0);
+
+ ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR,
+ (const uint8_t *)"foo", 3);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type);
+ CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client has Initial and Handshake keys */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
+
+ CU_ASSERT(spktlen > 0);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ ngtcp2_connection_close_error_set_transport_error_liberr(&ccerr, 0, NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+ CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client has all keys and has not confirmed handshake */
+ setup_handshake_client(&conn);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx,
+ null_iv, sizeof(null_iv), &hp_ctx);
+
+ conn->state = NGTCP2_CS_POST_HANDSHAKE;
+
+ ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ p = buf;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client has confirmed handshake */
+ setup_default_client(&conn);
+
+ ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+
+ /* Server has Initial and Handshake key */
+ setup_handshake_server(&conn);
+
+ conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+
+ init_initial_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx,
+ null_iv, &hp_ctx, sizeof(null_iv));
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ p = buf;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+ CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Server has all keys and has not confirmed handshake */
+ setup_handshake_server(&conn);
+
+ conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+
+ init_initial_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx,
+ null_iv, &hp_ctx, sizeof(null_iv));
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx,
+ null_iv, sizeof(null_iv), &hp_ctx);
+
+ conn->state = NGTCP2_CS_POST_HANDSHAKE;
+
+ ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ p = buf;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+
+ /* Server has confirmed handshake */
+ setup_default_server(&conn);
+
+ ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_write_application_close(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[1200];
+ ngtcp2_ssize spktlen, shdlen;
+ ngtcp2_pkt_hd hd;
+ const uint8_t *p;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ uint64_t app_err_code = 0;
+ ngtcp2_crypto_ctx crypto_ctx;
+ ngtcp2_connection_close_error ccerr;
+
+ /* Client only Initial key */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
+
+ CU_ASSERT(spktlen > 0);
+
+ ngtcp2_connection_close_error_set_application_error(
+ &ccerr, app_err_code, (const uint8_t *)"foo", 3);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type);
+ CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client has Initial and Handshake keys */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
+
+ CU_ASSERT(spktlen > 0);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+ CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client has all keys and has not confirmed handshake */
+ setup_handshake_client(&conn);
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx,
+ null_iv, sizeof(null_iv), &hp_ctx);
+
+ conn->state = NGTCP2_CS_POST_HANDSHAKE;
+
+ ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ p = buf;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client has confirmed handshake */
+ setup_default_client(&conn);
+
+ ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+
+ /* Server has Initial and Handshake key */
+ setup_handshake_server(&conn);
+
+ conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+
+ init_initial_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx,
+ null_iv, &hp_ctx, sizeof(null_iv));
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ p = buf;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+ CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen);
+
+ ngtcp2_conn_del(conn);
+
+ /* Server has all keys and has not confirmed handshake */
+ setup_handshake_server(&conn);
+
+ conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+
+ init_initial_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx);
+ ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx,
+ null_iv, &hp_ctx, sizeof(null_iv));
+
+ init_crypto_ctx(&crypto_ctx);
+
+ ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx);
+ conn->negotiated_version = conn->client_chosen_version;
+ ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+ ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx,
+ null_iv, sizeof(null_iv), &hp_ctx);
+
+ ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ p = buf;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type);
+
+ p += shdlen + (ngtcp2_ssize)hd.len;
+ spktlen -= shdlen + (ngtcp2_ssize)hd.len;
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+
+ /* Server has confirmed handshake */
+ setup_default_server(&conn);
+
+ ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code,
+ NULL, 0);
+
+ spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf,
+ sizeof(buf), &ccerr, 0);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen,
+ conn->dcid.current.cid.datalen);
+
+ CU_ASSERT(shdlen > 0);
+ CU_ASSERT(NGTCP2_PKT_1RTT == hd.type);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_rtb_reclaim_on_pto(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ int64_t stream_id;
+ uint8_t buf[2048];
+ ngtcp2_ssize nwrite;
+ ngtcp2_ssize spktlen;
+ size_t i;
+ size_t num_reclaim_pkt;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ksl_it it;
+
+ setup_default_client(&conn);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < 5; ++i) {
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 1024, 1);
+
+ CU_ASSERT(0 < spktlen);
+ }
+
+ CU_ASSERT(5 == ngtcp2_ksl_len(&conn->pktns.rtb.ents));
+
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, 3 * NGTCP2_SECONDS);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf),
+ 3 * NGTCP2_SECONDS);
+
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_ksl_begin(&conn->pktns.rtb.ents);
+ num_reclaim_pkt = 0;
+ for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
+ ++num_reclaim_pkt;
+ }
+ }
+
+ CU_ASSERT(1 == num_reclaim_pkt);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_rtb_reclaim_on_pto_datagram(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ int64_t stream_id;
+ uint8_t buf[2048];
+ ngtcp2_ssize nwrite;
+ ngtcp2_ssize spktlen;
+ size_t num_reclaim_pkt;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ksl_it it;
+ ngtcp2_vec datav;
+ int accepted;
+ ngtcp2_frame_chain *frc;
+
+ /* DATAGRAM frame must not be reclaimed on PTO */
+ setup_default_client(&conn);
+
+ conn->callbacks.ack_datagram = ack_datagram;
+ conn->remote.transport_params->max_datagram_frame_size = 65535;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 1024, 1);
+
+ CU_ASSERT(0 < spktlen);
+
+ datav.base = null_data;
+ datav.len = 10;
+
+ spktlen = ngtcp2_conn_writev_datagram(
+ conn, NULL, NULL, buf, sizeof(buf), &accepted,
+ NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 1000000007, &datav, 1, 1);
+
+ CU_ASSERT(accepted);
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(2 == ngtcp2_ksl_len(&conn->pktns.rtb.ents));
+
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, 3 * NGTCP2_SECONDS);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf),
+ 3 * NGTCP2_SECONDS);
+
+ CU_ASSERT(spktlen > 0);
+
+ it = ngtcp2_ksl_begin(&conn->pktns.rtb.ents);
+ num_reclaim_pkt = 0;
+ for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
+ ++num_reclaim_pkt;
+ for (frc = ent->frc; frc; frc = frc->next) {
+ CU_ASSERT(NGTCP2_FRAME_DATAGRAM != frc->fr.type);
+ }
+ }
+ }
+
+ CU_ASSERT(1 == num_reclaim_pkt);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_validate_ecn(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ ngtcp2_pkt_info pi;
+ size_t pktlen;
+ int rv;
+ ngtcp2_frame fr;
+ int64_t stream_id;
+ ngtcp2_ssize nwrite;
+ size_t i;
+ ngtcp2_tstamp t = 0;
+
+ setup_default_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+ CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+ CU_ASSERT(1 == conn->tx.ecn.validation_start_ts);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num);
+
+ fr.type = NGTCP2_FRAME_ACK_ECN;
+ fr.ack.largest_ack = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+ fr.ack.ecn.ect0 = 1;
+ fr.ack.ecn.ect1 = 0;
+ fr.ack.ecn.ce = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_ECN_STATE_CAPABLE == conn->tx.ecn.state);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 1024, 2);
+
+ CU_ASSERT(0 < spktlen);
+
+ /* Receiving ACK frame containing less ECN counts fails
+ validation */
+ fr.type = NGTCP2_FRAME_ACK_ECN;
+ fr.ack.largest_ack = 1;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+ fr.ack.ecn.ect0 = 0;
+ fr.ack.ecn.ect1 = 0;
+ fr.ack.ecn.ce = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 1024, 2);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn);
+
+ ngtcp2_conn_del(conn);
+
+ /* Receiving ACK frame without ECN counts invalidates ECN
+ capability */
+ setup_default_server(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+ CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+ CU_ASSERT(1 == conn->tx.ecn.validation_start_ts);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+
+ ngtcp2_conn_del(conn);
+
+ /* CE counts must be considered */
+ setup_default_client(&conn);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < 2; ++i) {
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 1024, 2);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+ }
+
+ CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+ CU_ASSERT(2 == conn->tx.ecn.validation_start_ts);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num);
+
+ fr.type = NGTCP2_FRAME_ACK_ECN;
+ fr.ack.largest_ack = 1;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 1;
+ fr.ack.rangecnt = 0;
+ fr.ack.ecn.ect0 = 1;
+ fr.ack.ecn.ect1 = 0;
+ fr.ack.ecn.ce = 1;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_ECN_STATE_CAPABLE == conn->tx.ecn.state);
+ CU_ASSERT(0 == ngtcp2_ksl_len(&conn->pktns.rtb.ents));
+
+ ngtcp2_conn_del(conn);
+
+ /* If increments of ECN counts is less than the number of
+ acknowledged ECN entries, ECN validation fails. */
+ setup_default_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+ CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+ CU_ASSERT(1 == conn->tx.ecn.validation_start_ts);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num);
+
+ fr.type = NGTCP2_FRAME_ACK_ECN;
+ fr.ack.largest_ack = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+ fr.ack.ecn.ect0 = 0;
+ fr.ack.ecn.ect1 = 1;
+ fr.ack.ecn.ce = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+
+ ngtcp2_conn_del(conn);
+
+ /* If ECT count is larger than the number of ECT marked packet, ECN
+ validation fails. */
+ setup_default_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+ CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+ CU_ASSERT(1 == conn->tx.ecn.validation_start_ts);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num);
+
+ fr.type = NGTCP2_FRAME_ACK_ECN;
+ fr.ack.largest_ack = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+ fr.ack.ecn.ect0 = 2;
+ fr.ack.ecn.ect1 = 0;
+ fr.ack.ecn.ce = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+
+ ngtcp2_conn_del(conn);
+
+ /* ECN validation fails if all ECN marked packets are lost */
+ setup_default_client(&conn);
+
+ t = 0;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS; ++i) {
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 25, t);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+ }
+
+ CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state);
+ CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == conn->tx.ecn.dgram_sent);
+
+ t += NGTCP2_MILLISECONDS;
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 25, t);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn);
+ CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state);
+ CU_ASSERT(0 == conn->tx.ecn.validation_start_ts);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num);
+ CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == conn->tx.ecn.dgram_sent);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.validation_pkt_lost);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ t += NGTCP2_MILLISECONDS;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t);
+
+ CU_ASSERT(0 == rv);
+
+ CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+ CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS ==
+ conn->pktns.tx.ecn.validation_pkt_lost);
+
+ ngtcp2_conn_del(conn);
+
+ /* ECN validation fails if all ECN marked packets sent in last 3 *
+ RTT are lost */
+ setup_default_client(&conn);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < 2; ++i) {
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf),
+ &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ stream_id, null_data, 25, 0);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+ }
+
+ CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+ CU_ASSERT(2 == conn->tx.ecn.dgram_sent);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+ null_data, 25, 3 * NGTCP2_SECONDS);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn);
+ CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state);
+ CU_ASSERT(0 == conn->tx.ecn.validation_start_ts);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num);
+ CU_ASSERT(2 == conn->pktns.tx.ecn.validation_pkt_sent);
+ CU_ASSERT(0 == conn->pktns.tx.ecn.validation_pkt_lost);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = 2;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen,
+ 4 * NGTCP2_SECONDS);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+ CU_ASSERT(2 == conn->pktns.tx.ecn.validation_pkt_lost);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_path_validation(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_ssize spktlen;
+ ngtcp2_tstamp t = 0;
+ int64_t pkt_num = 0;
+ ngtcp2_frame frs[4];
+ int rv;
+ ngtcp2_path_storage rpath, wpath;
+ ngtcp2_pv_entry *ent;
+
+ /* server starts path validation in NAT rebinding scenario. */
+ setup_default_server(&conn);
+
+ /* This will send NEW_CONNECTION_ID frames */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ frs[0].type = NGTCP2_FRAME_PING;
+
+ /* Just change remote port */
+ path_init(&rpath, 0, 0, 0, 1);
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &rpath.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->pv);
+ CU_ASSERT(0 == conn->pv->dcid.seq);
+ CU_ASSERT(ngtcp2_path_eq(&conn->pv->dcid.ps.path, &rpath.path));
+
+ ngtcp2_path_storage_zero(&wpath);
+ spktlen =
+ ngtcp2_conn_write_pkt(conn, &wpath.path, NULL, buf, sizeof(buf), ++t);
+
+ /* Server has not received enough bytes to pad probing packet. */
+ CU_ASSERT(1200 > spktlen);
+ CU_ASSERT(ngtcp2_path_eq(&rpath.path, &wpath.path));
+ CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->pv->ents.rb));
+
+ ent = ngtcp2_ringbuf_get(&conn->pv->ents.rb, 0);
+
+ CU_ASSERT(ent->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED);
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE);
+ CU_ASSERT(!(conn->pv->flags & NGTCP2_PV_FLAG_MTU_PROBE));
+
+ frs[0].type = NGTCP2_FRAME_PATH_RESPONSE;
+ memcpy(frs[0].path_response.data, ent->data, sizeof(ent->data));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &rpath.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ /* Start another path validation to probe least MTU */
+ CU_ASSERT(NULL != conn->pv);
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE);
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_MTU_PROBE);
+
+ ngtcp2_path_storage_zero(&wpath);
+ spktlen =
+ ngtcp2_conn_write_pkt(conn, &wpath.path, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(1200 <= spktlen);
+ CU_ASSERT(ngtcp2_path_eq(&rpath.path, &wpath.path));
+ CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->pv->ents.rb));
+
+ ent = ngtcp2_ringbuf_get(&conn->pv->ents.rb, 0);
+ frs[0].type = NGTCP2_FRAME_PATH_RESPONSE;
+ memcpy(frs[0].path_response.data, ent->data, sizeof(ent->data));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.rx.ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &rpath.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ /* Now perform another validation to old path */
+ CU_ASSERT(NULL != conn->pv);
+ CU_ASSERT(!(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE));
+ CU_ASSERT(!(conn->pv->flags & NGTCP2_PV_FLAG_MTU_PROBE));
+ CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_DONT_CARE);
+
+ ngtcp2_path_storage_zero(&wpath);
+ spktlen =
+ ngtcp2_conn_write_pkt(conn, &wpath.path, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(1200 <= spktlen);
+ CU_ASSERT(ngtcp2_path_eq(&null_path.path, &wpath.path));
+ CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->pv->ents.rb));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_early_data_sync_stream_data_limit(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[1024];
+ ngtcp2_ssize spktlen;
+ ngtcp2_ssize datalen;
+ int64_t bidi_stream_id, uni_stream_id;
+ int rv;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_transport_params params;
+ ngtcp2_strm *strm;
+ ngtcp2_tstamp t = 0;
+
+ setup_early_client(&conn);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &bidi_stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+ bidi_stream_id, null_data, 1024, ++t);
+
+ CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen);
+ CU_ASSERT(670 == datalen);
+
+ rv = ngtcp2_conn_open_uni_stream(conn, &uni_stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+ uni_stream_id, null_data, 1024, ++t);
+
+ CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen);
+ CU_ASSERT(958);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 198;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1,
+ NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_rx_key(conn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ memset(&params, 0, sizeof(params));
+ ngtcp2_cid_init(&params.initial_scid, conn->dcid.current.cid.data,
+ conn->dcid.current.cid.datalen);
+ ngtcp2_cid_init(&params.original_dcid, conn->rcid.data, conn->rcid.datalen);
+ params.max_udp_payload_size = 1200;
+ params.initial_max_stream_data_bidi_local =
+ conn->early.transport_params.initial_max_stream_data_bidi_local;
+ params.initial_max_stream_data_bidi_remote = 640 * 1024;
+ params.initial_max_stream_data_uni = 320 * 1024;
+ params.initial_max_data = conn->early.transport_params.initial_max_data;
+ params.initial_max_streams_bidi =
+ conn->early.transport_params.initial_max_streams_bidi;
+ params.initial_max_streams_uni =
+ conn->early.transport_params.initial_max_streams_uni;
+ params.active_connection_id_limit =
+ conn->early.transport_params.active_connection_id_limit;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_handshake_completed(conn);
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 < spktlen);
+
+ strm = ngtcp2_conn_find_stream(conn, bidi_stream_id);
+
+ CU_ASSERT(params.initial_max_stream_data_bidi_remote == strm->tx.max_offset);
+
+ strm = ngtcp2_conn_find_stream(conn, uni_stream_id);
+
+ CU_ASSERT(params.initial_max_stream_data_uni == strm->tx.max_offset);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_early_data_rejected(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[1024];
+ ngtcp2_ssize spktlen;
+ ngtcp2_ssize datalen;
+ int64_t bidi_stream_id, uni_stream_id;
+ int rv;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_transport_params params;
+ ngtcp2_tstamp t = 0;
+
+ setup_early_client(&conn);
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &bidi_stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+ bidi_stream_id, null_data, 1024, ++t);
+
+ CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen);
+ CU_ASSERT(670 == datalen);
+
+ rv = ngtcp2_conn_open_uni_stream(conn, &uni_stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_extend_max_offset(conn, 1000);
+ ngtcp2_conn_extend_max_streams_bidi(conn, 7);
+ ngtcp2_conn_extend_max_streams_uni(conn, 5);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+ &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+ uni_stream_id, null_data, 300, ++t);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(0 < conn->tx.offset);
+ CU_ASSERT(conn->local.transport_params.initial_max_data + 1000 ==
+ conn->rx.unsent_max_offset);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi + 7 ==
+ conn->remote.bidi.unsent_max_streams);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi + 7 ==
+ conn->remote.bidi.max_streams);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_uni + 5 ==
+ conn->remote.uni.unsent_max_streams);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_uni + 5 ==
+ conn->remote.uni.max_streams);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 198;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1,
+ NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv,
+ sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_rx_key(conn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ /* Stream limits in transport parameters can be reduced if early
+ data is rejected. */
+ memset(&params, 0, sizeof(params));
+ ngtcp2_cid_init(&params.initial_scid, conn->dcid.current.cid.data,
+ conn->dcid.current.cid.datalen);
+ ngtcp2_cid_init(&params.original_dcid, conn->rcid.data, conn->rcid.datalen);
+ params.max_udp_payload_size = 1200;
+ params.initial_max_stream_data_bidi_local =
+ conn->early.transport_params.initial_max_stream_data_bidi_local;
+ params.initial_max_stream_data_bidi_remote =
+ conn->early.transport_params.initial_max_stream_data_bidi_remote / 2;
+ params.initial_max_stream_data_uni = 0;
+ params.initial_max_data = conn->early.transport_params.initial_max_data;
+ params.initial_max_streams_bidi =
+ conn->early.transport_params.initial_max_streams_bidi;
+ params.initial_max_streams_uni =
+ conn->early.transport_params.initial_max_streams_uni;
+ params.active_connection_id_limit =
+ conn->early.transport_params.active_connection_id_limit;
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_handshake_completed(conn);
+ ngtcp2_conn_early_data_rejected(conn);
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, bidi_stream_id));
+ CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, uni_stream_id));
+ CU_ASSERT(0 == conn->tx.offset);
+ CU_ASSERT(conn->local.transport_params.initial_max_data ==
+ conn->rx.max_offset);
+ CU_ASSERT(conn->local.transport_params.initial_max_data ==
+ conn->rx.unsent_max_offset);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi ==
+ conn->remote.bidi.max_streams);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi ==
+ conn->remote.bidi.unsent_max_streams);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_uni ==
+ conn->remote.uni.max_streams);
+ CU_ASSERT(conn->local.transport_params.initial_max_streams_uni ==
+ conn->remote.uni.unsent_max_streams);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_keep_alive(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ ngtcp2_pkt_info pi;
+ ngtcp2_tstamp t = 0;
+ int rv;
+
+ setup_default_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 < spktlen);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ ngtcp2_conn_set_keep_alive_timeout(conn, 10 * NGTCP2_SECONDS);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), t);
+
+ CU_ASSERT(0 == spktlen);
+
+ t += 10 * NGTCP2_SECONDS;
+
+ rv = ngtcp2_conn_handle_expiry(conn, t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), t);
+
+ CU_ASSERT(0 < spktlen);
+ CU_ASSERT(t == conn->keep_alive.last_ts);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_retire_stale_bound_dcid(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ size_t pktlen;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_tstamp expiry;
+ int64_t pkt_num = 0;
+ ngtcp2_frame fr;
+ int rv;
+ ngtcp2_cid cid;
+ const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00};
+ const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff};
+ const uint8_t data[] = {0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8};
+
+ ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid));
+
+ setup_default_server(&conn);
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.new_connection_id.seq = 1;
+ fr.new_connection_id.retire_prior_to = 0;
+ fr.new_connection_id.cid = cid;
+ memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_PATH_CHALLENGE;
+ memcpy(fr.path_challenge.data, data, sizeof(fr.path_challenge.data));
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0);
+ CU_ASSERT(ngtcp2_ringbuf_len(&conn->dcid.bound.rb) > 0);
+
+ expiry = ngtcp2_conn_get_expiry(conn);
+
+ CU_ASSERT(UINT64_MAX != expiry);
+
+ t += 3 * ngtcp2_conn_get_pto(conn);
+
+ rv = ngtcp2_conn_handle_expiry(conn, t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_get_scid(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ ngtcp2_cid dcid, scid;
+ ngtcp2_callbacks cb;
+ const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00};
+ ngtcp2_cid scids[16];
+
+ dcid_init(&dcid);
+ dcid_init(&scid);
+
+ server_default_callbacks(&cb);
+ server_default_settings(&settings);
+
+ /* Without preferred address */
+ server_default_transport_params(&params);
+
+ ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+
+ CU_ASSERT(1 == ngtcp2_conn_get_num_scid(conn));
+
+ ngtcp2_conn_get_scid(conn, scids);
+
+ CU_ASSERT(ngtcp2_cid_eq(&scid, &scids[0]));
+
+ ngtcp2_conn_del(conn);
+
+ /* With preferred address */
+ server_default_transport_params(&params);
+ params.preferred_address_present = 1;
+ ngtcp2_cid_init(&params.preferred_address.cid, raw_cid, sizeof(raw_cid));
+
+ ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ /* mem = */ NULL, NULL);
+
+ CU_ASSERT(2 == ngtcp2_conn_get_num_scid(conn));
+
+ ngtcp2_conn_get_scid(conn, scids);
+
+ CU_ASSERT(ngtcp2_cid_eq(&scid, &scids[0]));
+ CU_ASSERT(ngtcp2_cid_eq(&params.preferred_address.cid, &scids[1]));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_stream_close(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ uint8_t buf[2048];
+ ngtcp2_frame frs[2];
+ size_t pktlen;
+ int64_t pkt_num = 0;
+ my_user_data ud;
+ ngtcp2_strm *strm;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_ssize spktlen;
+ int64_t stream_id;
+
+ /* Receive RESET_STREAM and STOP_SENDING from client */
+ setup_default_server(&conn);
+ conn->callbacks.stream_close = stream_close;
+ conn->user_data = &ud;
+
+ open_stream(conn, 0);
+
+ frs[0].type = NGTCP2_FRAME_RESET_STREAM;
+ frs[0].reset_stream.stream_id = 0;
+ frs[0].reset_stream.app_error_code = NGTCP2_APP_ERR01;
+ frs[0].reset_stream.final_size = 999;
+
+ frs[1].type = NGTCP2_FRAME_STOP_SENDING;
+ frs[1].stop_sending.stream_id = 0;
+ frs[1].stop_sending.app_error_code = NGTCP2_APP_ERR02;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.tx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ strm = ngtcp2_conn_find_stream(conn, 0);
+
+ CU_ASSERT(NGTCP2_APP_ERR01 == strm->app_error_code);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT((size_t)spktlen < sizeof(buf));
+
+ frs[0].type = NGTCP2_FRAME_ACK;
+ frs[0].ack.largest_ack = 0;
+ frs[0].ack.ack_delay = 0;
+ frs[0].ack.first_ack_range = 0;
+ frs[0].ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.tx.ckm);
+
+ ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE;
+ ud.stream_close.stream_id = -1;
+ ud.stream_close.app_error_code = 0;
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET &
+ ud.stream_close.flags);
+ CU_ASSERT(0 == ud.stream_close.stream_id);
+ CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client sends STOP_SENDING and then STREAM and fin */
+ pkt_num = 0;
+
+ setup_default_server(&conn);
+ conn->callbacks.stream_close = stream_close;
+ conn->callbacks.recv_stream_data = recv_stream_data;
+ conn->user_data = &ud;
+
+ frs[0].type = NGTCP2_FRAME_STOP_SENDING;
+ frs[0].stop_sending.stream_id = 0;
+ frs[0].stop_sending.app_error_code = NGTCP2_APP_ERR01;
+
+ frs[1].type = NGTCP2_FRAME_STREAM;
+ frs[1].stream.flags = 0;
+ frs[1].stream.fin = 1;
+ frs[1].stream.stream_id = 0;
+ frs[1].stream.offset = 0;
+ frs[1].stream.datacnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.tx.ckm);
+
+ ud.stream_data.stream_id = -1;
+ ud.stream_data.flags = NGTCP2_STREAM_DATA_FLAG_NONE;
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ud.stream_data.stream_id);
+ CU_ASSERT((ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN) != 0);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+ CU_ASSERT((size_t)spktlen < sizeof(buf));
+
+ frs[0].type = NGTCP2_FRAME_ACK;
+ frs[0].ack.largest_ack = 0;
+ frs[0].ack.ack_delay = 0;
+ frs[0].ack.first_ack_range = 0;
+ frs[0].ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.tx.ckm);
+
+ ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE;
+ ud.stream_close.stream_id = -1;
+ ud.stream_close.app_error_code = 0;
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET &
+ ud.stream_close.flags);
+ CU_ASSERT(0 == ud.stream_close.stream_id);
+ CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client calls ngtcp2_conn_shutdown_stream, and before sending
+ STOP_SENDING, it receives STREAM with fin bit set. */
+ pkt_num = 0;
+
+ setup_default_client(&conn);
+ conn->callbacks.stream_close = stream_close;
+ conn->user_data = &ud;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id,
+ null_data, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ frs[0].type = NGTCP2_FRAME_ACK;
+ frs[0].ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ frs[0].ack.ack_delay = 0;
+ frs[0].ack.first_ack_range = 0;
+ frs[0].ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.tx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_shutdown_stream(conn, stream_id, NGTCP2_APP_ERR01);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_STREAM;
+ frs[0].stream.flags = 0;
+ frs[0].stream.fin = 1;
+ frs[0].stream.stream_id = stream_id;
+ frs[0].stream.offset = 0;
+ frs[0].stream.datacnt = 1;
+ frs[0].stream.data[0].len = 97;
+ frs[0].stream.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1,
+ conn->pktns.crypto.tx.ckm);
+
+ ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE;
+ ud.stream_close.stream_id = -1;
+ ud.stream_close.app_error_code = 0;
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET &
+ ud.stream_close.flags);
+ CU_ASSERT(stream_id == ud.stream_close.stream_id);
+ CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client sends STREAM fin and then RESET_STREAM. It receives ACK
+ for the STREAM frame, then response fin. No ACK for
+ RESET_STREAM. */
+ pkt_num = 0;
+
+ setup_default_client(&conn);
+ conn->callbacks.stream_close = stream_close;
+ conn->user_data = &ud;
+
+ rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id,
+ null_data, 1, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01);
+
+ CU_ASSERT(0 == rv);
+
+ frs[0].type = NGTCP2_FRAME_STREAM;
+ frs[0].stream.flags = 0;
+ frs[0].stream.fin = 1;
+ frs[0].stream.stream_id = stream_id;
+ frs[0].stream.offset = 0;
+ frs[0].stream.datacnt = 0;
+
+ frs[1].type = NGTCP2_FRAME_ACK;
+ frs[1].ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ frs[1].ack.ack_delay = 0;
+ frs[1].ack.first_ack_range = 0;
+ frs[1].ack.rangecnt = 0;
+
+ spktlen =
+ ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
+ NGTCP2_WRITE_STREAM_FLAG_NONE, -1, NULL, 0, ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2,
+ conn->pktns.crypto.tx.ckm);
+
+ ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE;
+ ud.stream_close.stream_id = -1;
+ ud.stream_close.app_error_code = 0;
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET &
+ ud.stream_close.flags);
+ CU_ASSERT(stream_id == ud.stream_close.stream_id);
+ CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_buffer_pkt(void) {
+ ngtcp2_conn *conn;
+ int rv;
+ uint8_t buf[2048];
+ ngtcp2_frame fr;
+ ngtcp2_frame frs[2];
+ size_t pktlen, in_pktlen;
+ int64_t pkt_num = 0;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ ngtcp2_ksl_it it;
+ ngtcp2_pkt_chain *pc;
+
+ /* Server should buffer Short packet if it does not complete
+ handshake even if it has application tx key. */
+ setup_handshake_server(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1193;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), pkt_num++,
+ NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm);
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+
+ assert(0 == rv);
+
+ rv = ngtcp2_conn_install_rx_key(conn, null_secret, sizeof(null_secret),
+ &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx);
+
+ assert(0 == rv);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_PING;
+
+ in_pktlen = write_initial_pkt(
+ buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), pkt_num++,
+ NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm);
+
+ frs[0].type = NGTCP2_FRAME_PING;
+ frs[1].type = NGTCP2_FRAME_PADDING;
+ frs[1].padding.len = 1200;
+
+ pktlen = write_pkt(buf + in_pktlen, sizeof(buf) - in_pktlen, &conn->oscid,
+ pkt_num++, frs, 2, &null_ckm);
+
+ CU_ASSERT(!conn->pktns.rx.buffed_pkts);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+ in_pktlen + pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ pc = conn->pktns.rx.buffed_pkts;
+
+ CU_ASSERT(pktlen == pc->pktlen);
+ CU_ASSERT(in_pktlen + pktlen == pc->dgramlen);
+
+ it = ngtcp2_acktr_get(&conn->pktns.acktr);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_handshake_timeout(void) {
+ ngtcp2_conn *conn;
+ int rv;
+
+ /* handshake has just timed out */
+ setup_handshake_server(&conn);
+
+ rv = ngtcp2_conn_handle_expiry(conn,
+ conn->local.settings.initial_ts +
+ conn->local.settings.handshake_timeout);
+
+ CU_ASSERT(NGTCP2_ERR_HANDSHAKE_TIMEOUT == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* handshake is still in progress */
+ setup_handshake_server(&conn);
+
+ rv = ngtcp2_conn_handle_expiry(
+ conn, conn->local.settings.initial_ts +
+ conn->local.settings.handshake_timeout - 1);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* handshake timeout should be ignored after handshake has
+ completed. */
+ setup_default_server(&conn);
+
+ rv = ngtcp2_conn_handle_expiry(conn,
+ conn->local.settings.initial_ts +
+ conn->local.settings.handshake_timeout);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_get_connection_close_error(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_frame frs[2];
+ size_t pktlen;
+ uint8_t reason[2048];
+ ngtcp2_tstamp t = 0;
+ int64_t pkt_num = 0;
+ int rv;
+ ngtcp2_connection_close_error ccerr;
+
+ memset(reason, 'a', sizeof(reason));
+
+ setup_default_server(&conn);
+
+ /* Record the last error. */
+ frs[0].type = NGTCP2_FRAME_CONNECTION_CLOSE_APP;
+ frs[0].connection_close.error_code = 1;
+ frs[0].connection_close.frame_type = 99;
+ frs[0].connection_close.reasonlen = 10;
+ frs[0].connection_close.reason = reason;
+
+ frs[1].type = NGTCP2_FRAME_CONNECTION_CLOSE;
+ frs[1].connection_close.error_code = NGTCP2_PROTOCOL_VIOLATION;
+ frs[1].connection_close.frame_type = 1000000007;
+ frs[1].connection_close.reasonlen =
+ NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN + 1;
+ frs[1].connection_close.reason = reason;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs,
+ ngtcp2_arraylen(frs), conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
+
+ ngtcp2_conn_get_connection_close_error(conn, &ccerr);
+
+ CU_ASSERT(NGTCP2_PROTOCOL_VIOLATION == ccerr.error_code);
+ CU_ASSERT(NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT == ccerr.type);
+ CU_ASSERT(1000000007 == ccerr.frame_type);
+ CU_ASSERT(0 == memcmp(reason, ccerr.reason, ccerr.reasonlen));
+ CU_ASSERT(NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN == ccerr.reasonlen);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_version_negotiation(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_frame fr;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_ssize spktlen;
+ int64_t pkt_num = 0;
+ size_t pktlen;
+ int rv;
+ ngtcp2_transport_params remote_params;
+ uint8_t other_versions[sizeof(uint32_t) * 2];
+ uint32_t version;
+
+ ngtcp2_put_uint32be(&other_versions[0], NGTCP2_PROTO_VER_V1);
+ ngtcp2_put_uint32be(&other_versions[4], NGTCP2_PROTO_VER_V2_DRAFT);
+
+ /* Client sees the change version in Initial packet which contains
+ CRYPTO frame. It generates new Initial keys and sets negotiated
+ version. */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 133;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), pkt_num++,
+ NGTCP2_PROTO_VER_V2_DRAFT, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->negotiated_version);
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->vneg.version);
+ CU_ASSERT(conn->vneg.rx.ckm != NULL);
+ CU_ASSERT(conn->vneg.tx.ckm != NULL);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ ngtcp2_get_uint32(&version, &buf[1]);
+
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == version);
+
+ ngtcp2_conn_del(conn);
+
+ /* Client receives Initial packet which does not change version and
+ does not contain CRYPTO frame. It leaves negotiated version
+ unchanged. */
+ setup_handshake_client(&conn);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_PADDING;
+ fr.padding.len = 1;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), pkt_num++,
+ NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == conn->negotiated_version);
+ CU_ASSERT(0 == conn->vneg.version);
+ CU_ASSERT(conn->vneg.rx.ckm == NULL);
+ CU_ASSERT(conn->vneg.tx.ckm == NULL);
+
+ ngtcp2_conn_del(conn);
+
+ /* Server sees client supports QUIC v2. It chooses QUIC v2 as the
+ negotiated version, and generates new Initial keys. */
+ setup_handshake_server(&conn);
+
+ conn->callbacks.recv_client_initial =
+ recv_client_initial_no_remote_transport_params;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1233;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid,
+ ngtcp2_conn_get_dcid(conn), pkt_num++,
+ NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_transport_params_default(&remote_params);
+ ngtcp2_cid_init(&remote_params.initial_scid, conn->dcid.current.cid.data,
+ conn->dcid.current.cid.datalen);
+ remote_params.version_info_present = 1;
+ remote_params.version_info.chosen_version = NGTCP2_PROTO_VER_V1;
+ remote_params.version_info.other_versions = other_versions;
+ remote_params.version_info.other_versionslen = sizeof(other_versions);
+
+ rv = ngtcp2_conn_set_remote_transport_params(conn, &remote_params);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->negotiated_version);
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->vneg.version);
+ CU_ASSERT(conn->vneg.rx.ckm != NULL);
+ CU_ASSERT(conn->vneg.tx.ckm != NULL);
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ ngtcp2_get_uint32(&version, &buf[1]);
+
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == version);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_server_negotiate_version(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_version_info version_info = {0};
+ uint8_t client_other_versions[sizeof(uint32_t) * 2];
+
+ setup_handshake_server(&conn);
+
+ version_info.chosen_version = conn->client_chosen_version;
+
+ /* Empty version_info.other_versions */
+ version_info.other_versions = NULL;
+ version_info.other_versionslen = 0;
+
+ CU_ASSERT(conn->client_chosen_version ==
+ ngtcp2_conn_server_negotiate_version(conn, &version_info));
+
+ /* version_info.other_versions and preferred_versions do not share
+ any version. */
+ ngtcp2_put_uint32be(&client_other_versions[0], 0xff000001);
+ ngtcp2_put_uint32be(&client_other_versions[4], 0xff000002);
+
+ version_info.other_versions = client_other_versions;
+ version_info.other_versionslen = sizeof(uint32_t) * 2;
+
+ CU_ASSERT(conn->client_chosen_version ==
+ ngtcp2_conn_server_negotiate_version(conn, &version_info));
+
+ /* version_info.other_versions and preferred_versions share the
+ version. */
+ ngtcp2_put_uint32be(&client_other_versions[0], 0xff000001);
+ ngtcp2_put_uint32be(&client_other_versions[4], NGTCP2_PROTO_VER_V2_DRAFT);
+
+ version_info.other_versions = client_other_versions;
+ version_info.other_versionslen = sizeof(uint32_t) * 2;
+
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT ==
+ ngtcp2_conn_server_negotiate_version(conn, &version_info));
+
+ ngtcp2_conn_del(conn);
+
+ /* Without preferred_versions */
+ setup_handshake_server(&conn);
+
+ ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions);
+ conn->vneg.preferred_versions = NULL;
+ conn->vneg.preferred_versionslen = 0;
+
+ ngtcp2_put_uint32be(&client_other_versions[0], 0xff000001);
+ ngtcp2_put_uint32be(&client_other_versions[4], NGTCP2_PROTO_VER_V2_DRAFT);
+
+ version_info.other_versions = client_other_versions;
+ version_info.other_versionslen = sizeof(uint32_t) * 2;
+
+ CU_ASSERT(conn->client_chosen_version ==
+ ngtcp2_conn_server_negotiate_version(conn, &version_info));
+
+ ngtcp2_conn_del(conn);
+
+ /* original version is the most preferred version */
+ setup_handshake_server(&conn);
+
+ conn->vneg.preferred_versions[0] = NGTCP2_PROTO_VER_V1;
+ conn->vneg.preferred_versions[1] = NGTCP2_PROTO_VER_V2_DRAFT;
+
+ ngtcp2_put_uint32be(&client_other_versions[0], NGTCP2_PROTO_VER_V2_DRAFT);
+ ngtcp2_put_uint32be(&client_other_versions[4], NGTCP2_PROTO_VER_V1);
+
+ version_info.other_versions = client_other_versions;
+ version_info.other_versionslen = sizeof(uint32_t) * 2;
+
+ CU_ASSERT(conn->client_chosen_version ==
+ ngtcp2_conn_server_negotiate_version(conn, &version_info));
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_pmtud_loss(void) {
+ ngtcp2_conn *conn;
+ uint8_t buf[2048];
+ ngtcp2_ssize spktlen;
+ uint64_t t = 0;
+ ngtcp2_frame fr;
+ int64_t pkt_num = 0;
+ size_t pktlen;
+ int rv;
+
+ setup_default_client(&conn);
+
+ ngtcp2_conn_start_pmtud(conn);
+
+ /* This sends PMTUD packet. */
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(1406 == spktlen);
+
+ t += NGTCP2_SECONDS;
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(spktlen > 0);
+
+ fr.type = NGTCP2_FRAME_ACK;
+ fr.ack.largest_ack = conn->pktns.tx.last_pkt_num;
+ fr.ack.ack_delay = 0;
+ fr.ack.first_ack_range = 0;
+ fr.ack.rangecnt = 0;
+
+ pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, pkt_num++, &fr, 1,
+ conn->pktns.crypto.rx.ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == conn->pktns.rtb.num_lost_pkts);
+ CU_ASSERT(1 == conn->pktns.rtb.num_lost_pmtud_pkts);
+ CU_ASSERT(0 == conn->pktns.rtb.cc_bytes_in_flight);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_conn_amplification(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_frame fr;
+ size_t pktlen;
+ uint8_t buf[2048];
+ ngtcp2_cid rcid;
+ int64_t pkt_num = 0;
+ ngtcp2_tstamp t = 0;
+ ngtcp2_ssize spktlen;
+ int rv;
+
+ rcid_init(&rcid);
+
+ /* ACK only frame should not be sent due to amplification limit. */
+ setup_early_server(&conn);
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(
+ buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
+ conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.stream_id = 4;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 111;
+ fr.stream.data[0].base = null_data;
+
+ pktlen =
+ write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn),
+ ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm);
+
+ rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
+
+ CU_ASSERT(0 == rv);
+
+ /* Adjust condition so that the execution path goes into sending ACK
+ only frame. */
+ conn->dcid.current.bytes_sent = conn->dcid.current.bytes_recv * 3 - 1;
+ conn->cstat.bytes_in_flight = conn->cstat.cwnd;
+
+ spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
+
+ CU_ASSERT(0 == spktlen);
+
+ ngtcp2_conn_del(conn);
+}
+
+typedef struct failmalloc {
+ size_t nmalloc;
+ size_t fail_start;
+} failmalloc;
+
+static void *failmalloc_malloc(size_t size, void *user_data) {
+ failmalloc *mc = user_data;
+
+ if (mc->fail_start <= ++mc->nmalloc) {
+ return NULL;
+ }
+
+ return malloc(size);
+}
+
+static void failmalloc_free(void *ptr, void *user_data) {
+ (void)user_data;
+
+ free(ptr);
+}
+
+static void *failmalloc_calloc(size_t nmemb, size_t size, void *user_data) {
+ failmalloc *mc = user_data;
+
+ if (mc->fail_start <= ++mc->nmalloc) {
+ return NULL;
+ }
+
+ return calloc(nmemb, size);
+}
+
+static void *failmalloc_realloc(void *ptr, size_t size, void *user_data) {
+ failmalloc *mc = user_data;
+
+ if (mc->fail_start <= ++mc->nmalloc) {
+ return NULL;
+ }
+
+ return realloc(ptr, size);
+}
+
+void test_ngtcp2_conn_new_failmalloc(void) {
+ ngtcp2_conn *conn;
+ ngtcp2_callbacks cb;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params params;
+ failmalloc mc;
+ ngtcp2_mem mem = {
+ &mc,
+ failmalloc_malloc,
+ failmalloc_free,
+ failmalloc_calloc,
+ failmalloc_realloc,
+ };
+ ngtcp2_vec token = {
+ (uint8_t *)"token",
+ sizeof("token") - 1,
+ };
+ uint32_t preferred_versions[] = {
+ NGTCP2_PROTO_VER_V1,
+ NGTCP2_PROTO_VER_V2_DRAFT,
+ };
+ uint32_t other_versions[] = {
+ NGTCP2_PROTO_VER_V2_DRAFT,
+ NGTCP2_PROTO_VER_V1,
+ 0x5a9aeaca,
+ };
+ ngtcp2_cid dcid, scid;
+ int rv;
+ size_t i;
+ size_t nmalloc;
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ ngtcp2_settings_default(&settings);
+ ngtcp2_transport_params_default(&params);
+
+ settings.qlog.write = qlog_write;
+ settings.token = token;
+ settings.preferred_versions = preferred_versions;
+ settings.preferred_versionslen = ngtcp2_arraylen(preferred_versions);
+ settings.other_versions = other_versions;
+ settings.other_versionslen = ngtcp2_arraylen(other_versions);
+
+ /* server */
+ server_default_callbacks(&cb);
+
+ mc.nmalloc = 0;
+ mc.fail_start = SIZE_MAX;
+
+ rv = ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ &mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ nmalloc = mc.nmalloc;
+
+ for (i = 0; i <= nmalloc; ++i) {
+ mc.nmalloc = 0;
+ mc.fail_start = i;
+
+ rv = ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ &mem, NULL);
+
+ CU_ASSERT(NGTCP2_ERR_NOMEM == rv);
+ }
+
+ mc.nmalloc = 0;
+ mc.fail_start = nmalloc + 1;
+
+ rv = ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ &mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ /* client */
+ client_default_callbacks(&cb);
+
+ mc.nmalloc = 0;
+ mc.fail_start = SIZE_MAX;
+
+ rv = ngtcp2_conn_client_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ &mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+
+ nmalloc = mc.nmalloc;
+
+ for (i = 0; i <= nmalloc; ++i) {
+ mc.nmalloc = 0;
+ mc.fail_start = i;
+
+ rv = ngtcp2_conn_client_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ &mem, NULL);
+
+ CU_ASSERT(NGTCP2_ERR_NOMEM == rv);
+ }
+
+ mc.nmalloc = 0;
+ mc.fail_start = nmalloc + 1;
+
+ rv = ngtcp2_conn_client_new(&conn, &dcid, &scid, &null_path.path,
+ NGTCP2_PROTO_VER_V1, &cb, &settings, &params,
+ &mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_conn_del(conn);
+}
+
+void test_ngtcp2_accept(void) {
+ size_t pktlen;
+ uint8_t buf[2048];
+ ngtcp2_cid dcid, scid;
+ ngtcp2_frame fr;
+ int rv;
+ ngtcp2_pkt_hd hd;
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ /* Initial packet */
+ memset(&hd, 0, sizeof(hd));
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &dcid, &scid, 0,
+ NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm);
+
+ CU_ASSERT(pktlen >= 1200);
+
+ rv = ngtcp2_accept(&hd, buf, pktlen);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &hd.dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&scid, &hd.scid));
+ CU_ASSERT(0 == hd.token.len);
+ CU_ASSERT(hd.len > 0);
+ CU_ASSERT(NGTCP2_PROTO_VER_V1 == hd.version);
+ CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type);
+ CU_ASSERT(hd.flags & NGTCP2_PKT_FLAG_LONG_FORM);
+
+ /* 0RTT packet */
+ memset(&hd, 0, sizeof(hd));
+
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.flags = 0;
+ fr.stream.stream_id = 0;
+ fr.stream.fin = 0;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = 1200;
+ fr.stream.data[0].base = null_data;
+
+ pktlen = write_0rtt_pkt(buf, sizeof(buf), &dcid, &scid, 1,
+ NGTCP2_PROTO_VER_V1, &fr, 1, &null_ckm);
+
+ CU_ASSERT(pktlen >= 1200);
+
+ rv = ngtcp2_accept(&hd, buf, pktlen);
+
+ CU_ASSERT(NGTCP2_ERR_RETRY == rv);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &hd.dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&scid, &hd.scid));
+ CU_ASSERT(0 == hd.token.len);
+ CU_ASSERT(hd.len > 0);
+ CU_ASSERT(NGTCP2_PROTO_VER_V1 == hd.version);
+ CU_ASSERT(NGTCP2_PKT_0RTT == hd.type);
+ CU_ASSERT(hd.flags & NGTCP2_PKT_FLAG_LONG_FORM);
+
+ /* Unknown version */
+ memset(&hd, 0, sizeof(hd));
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &dcid, &scid, 0, 0x2, NULL, 0,
+ &fr, 1, &null_ckm);
+
+ CU_ASSERT(pktlen >= 1200);
+
+ rv = ngtcp2_accept(&hd, buf, pktlen);
+
+ /* Unknown version should be filtered out by earlier call of
+ ngtcp2_pkt_decode_version_cid, that is, only supported versioned
+ packet should be passed to ngtcp2_accept. */
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+
+ /* Unknown version and the UDP payload size is less than
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE. */
+ memset(&hd, 0, sizeof(hd));
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1127;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_initial_pkt(buf, sizeof(buf), &dcid, &scid, 0, 0x2, NULL, 0,
+ &fr, 1, &null_ckm);
+
+ CU_ASSERT(1199 == pktlen);
+
+ rv = ngtcp2_accept(&hd, buf, pktlen);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+
+ /* Short packet */
+ memset(&hd, 0, sizeof(hd));
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = 1200;
+ fr.crypto.data[0].base = null_data;
+
+ pktlen = write_pkt(buf, sizeof(buf), &dcid, 0, &fr, 1, &null_ckm);
+
+ CU_ASSERT(pktlen >= 1200);
+
+ rv = ngtcp2_accept(&hd, buf, pktlen);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+
+ /* Unable to decode packet header */
+ memset(&hd, 0, sizeof(hd));
+
+ memset(buf, 0, 4);
+ buf[0] = NGTCP2_HEADER_FORM_BIT;
+
+ rv = ngtcp2_accept(&hd, buf, 4);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+}
+
+void test_ngtcp2_select_version(void) {
+ CU_ASSERT(0 == ngtcp2_select_version(NULL, 0, NULL, 0));
+
+ {
+ uint32_t preferred_versions[] = {NGTCP2_PROTO_VER_V1,
+ NGTCP2_PROTO_VER_V2_DRAFT};
+ uint32_t offered_versions[] = {0x00000004, 0x00000003,
+ NGTCP2_PROTO_VER_V2_DRAFT};
+
+ CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT ==
+ ngtcp2_select_version(
+ preferred_versions, ngtcp2_arraylen(preferred_versions),
+ offered_versions, ngtcp2_arraylen(offered_versions)));
+ }
+
+ {
+ uint32_t preferred_versions[] = {NGTCP2_PROTO_VER_V1,
+ NGTCP2_PROTO_VER_V2_DRAFT};
+ uint32_t offered_versions[] = {0x00000004, 0x00000003};
+
+ CU_ASSERT(0 == ngtcp2_select_version(
+ preferred_versions, ngtcp2_arraylen(preferred_versions),
+ offered_versions, ngtcp2_arraylen(offered_versions)));
+ }
+}
+
+void test_ngtcp2_pkt_write_connection_close(void) {
+ ngtcp2_ssize spktlen;
+ uint8_t buf[1200];
+ ngtcp2_cid dcid, scid;
+ ngtcp2_crypto_aead aead = {0, NGTCP2_INITIAL_AEAD_OVERHEAD};
+ ngtcp2_crypto_cipher hp_mask = {0};
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ spktlen = ngtcp2_pkt_write_connection_close(
+ buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &dcid, &scid, NGTCP2_INVALID_TOKEN,
+ (const uint8_t *)"foo", 3, null_encrypt, &aead, &aead_ctx, null_iv,
+ null_hp_mask, &hp_mask, &hp_ctx);
+
+ CU_ASSERT(spktlen > 0);
+
+ spktlen = ngtcp2_pkt_write_connection_close(
+ buf, 16, NGTCP2_PROTO_VER_V1, &dcid, &scid, NGTCP2_INVALID_TOKEN, NULL, 0,
+ null_encrypt, &aead, &aead_ctx, null_iv, null_hp_mask, &hp_mask, &hp_ctx);
+
+ CU_ASSERT(NGTCP2_ERR_NOBUF == spktlen);
+}
diff --git a/tests/ngtcp2_conn_test.h b/tests/ngtcp2_conn_test.h
new file mode 100644
index 0000000..b3c2b3a
--- /dev/null
+++ b/tests/ngtcp2_conn_test.h
@@ -0,0 +1,103 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CONN_TEST_H
+#define NGTCP2_CONN_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void init_static_path(void);
+
+void test_ngtcp2_conn_stream_open_close(void);
+void test_ngtcp2_conn_stream_rx_flow_control(void);
+void test_ngtcp2_conn_stream_rx_flow_control_error(void);
+void test_ngtcp2_conn_stream_tx_flow_control(void);
+void test_ngtcp2_conn_rx_flow_control(void);
+void test_ngtcp2_conn_rx_flow_control_error(void);
+void test_ngtcp2_conn_tx_flow_control(void);
+void test_ngtcp2_conn_shutdown_stream_write(void);
+void test_ngtcp2_conn_recv_reset_stream(void);
+void test_ngtcp2_conn_recv_stop_sending(void);
+void test_ngtcp2_conn_recv_conn_id_omitted(void);
+void test_ngtcp2_conn_short_pkt_type(void);
+void test_ngtcp2_conn_recv_stateless_reset(void);
+void test_ngtcp2_conn_recv_retry(void);
+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_retransmit_protected(void);
+void test_ngtcp2_conn_send_max_stream_data(void);
+void test_ngtcp2_conn_recv_stream_data(void);
+void test_ngtcp2_conn_recv_ping(void);
+void test_ngtcp2_conn_recv_max_stream_data(void);
+void test_ngtcp2_conn_send_early_data(void);
+void test_ngtcp2_conn_recv_early_data(void);
+void test_ngtcp2_conn_recv_compound_pkt(void);
+void test_ngtcp2_conn_pkt_payloadlen(void);
+void test_ngtcp2_conn_writev_stream(void);
+void test_ngtcp2_conn_writev_datagram(void);
+void test_ngtcp2_conn_recv_datagram(void);
+void test_ngtcp2_conn_recv_new_connection_id(void);
+void test_ngtcp2_conn_recv_retire_connection_id(void);
+void test_ngtcp2_conn_server_path_validation(void);
+void test_ngtcp2_conn_client_connection_migration(void);
+void test_ngtcp2_conn_recv_path_challenge(void);
+void test_ngtcp2_conn_key_update(void);
+void test_ngtcp2_conn_crypto_buffer_exceeded(void);
+void test_ngtcp2_conn_handshake_probe(void);
+void test_ngtcp2_conn_handshake_loss(void);
+void test_ngtcp2_conn_recv_client_initial_retry(void);
+void test_ngtcp2_conn_recv_client_initial_token(void);
+void test_ngtcp2_conn_get_active_dcid(void);
+void test_ngtcp2_conn_recv_version_negotiation(void);
+void test_ngtcp2_conn_send_initial_token(void);
+void test_ngtcp2_conn_set_remote_transport_params(void);
+void test_ngtcp2_conn_write_connection_close(void);
+void test_ngtcp2_conn_write_application_close(void);
+void test_ngtcp2_conn_rtb_reclaim_on_pto(void);
+void test_ngtcp2_conn_rtb_reclaim_on_pto_datagram(void);
+void test_ngtcp2_conn_validate_ecn(void);
+void test_ngtcp2_conn_path_validation(void);
+void test_ngtcp2_conn_early_data_sync_stream_data_limit(void);
+void test_ngtcp2_conn_early_data_rejected(void);
+void test_ngtcp2_conn_keep_alive(void);
+void test_ngtcp2_conn_retire_stale_bound_dcid(void);
+void test_ngtcp2_conn_get_scid(void);
+void test_ngtcp2_conn_stream_close(void);
+void test_ngtcp2_conn_buffer_pkt(void);
+void test_ngtcp2_conn_handshake_timeout(void);
+void test_ngtcp2_conn_get_connection_close_error(void);
+void test_ngtcp2_conn_version_negotiation(void);
+void test_ngtcp2_conn_server_negotiate_version(void);
+void test_ngtcp2_conn_pmtud_loss(void);
+void test_ngtcp2_conn_amplification(void);
+void test_ngtcp2_conn_new_failmalloc(void);
+void test_ngtcp2_accept(void);
+void test_ngtcp2_select_version(void);
+void test_ngtcp2_pkt_write_connection_close(void);
+
+#endif /* NGTCP2_CONN_TEST_H */
diff --git a/tests/ngtcp2_conv_test.c b/tests/ngtcp2_conv_test.c
new file mode 100644
index 0000000..7b92ec7
--- /dev/null
+++ b/tests/ngtcp2_conv_test.c
@@ -0,0 +1,430 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_conv_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_conv.h"
+#include "ngtcp2_net.h"
+#include "ngtcp2_test_helper.h"
+
+void test_ngtcp2_get_varint(void) {
+ uint8_t buf[256];
+ const uint8_t *p;
+ uint64_t n;
+ int64_t s;
+
+ /* 0 */
+ n = 1;
+ p = ngtcp2_put_uvarint(buf, 0);
+
+ CU_ASSERT(1 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(1 == p - buf);
+ CU_ASSERT(0 == n);
+
+ /* 63 */
+ n = 0;
+ p = ngtcp2_put_uvarint(buf, 63);
+
+ CU_ASSERT(1 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(1 == p - buf);
+ CU_ASSERT(63 == n);
+
+ /* 64 */
+ n = 0;
+ p = ngtcp2_put_uvarint(buf, 64);
+
+ CU_ASSERT(2 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(2 == p - buf);
+ CU_ASSERT(64 == n);
+
+ /* 16383 */
+ n = 0;
+ p = ngtcp2_put_uvarint(buf, 16383);
+
+ CU_ASSERT(2 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(2 == p - buf);
+ CU_ASSERT(16383 == n);
+
+ /* 16384 */
+ n = 0;
+ p = ngtcp2_put_uvarint(buf, 16384);
+
+ CU_ASSERT(4 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(4 == p - buf);
+ CU_ASSERT(16384 == n);
+
+ /* 1073741823 */
+ n = 0;
+ p = ngtcp2_put_uvarint(buf, 1073741823);
+
+ CU_ASSERT(4 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(4 == p - buf);
+ CU_ASSERT(1073741823 == n);
+
+ /* 1073741824 */
+ n = 0;
+ p = ngtcp2_put_uvarint(buf, 1073741824);
+
+ CU_ASSERT(8 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(8 == p - buf);
+ CU_ASSERT(1073741824 == n);
+
+ /* 4611686018427387903 */
+ n = 0;
+ p = ngtcp2_put_uvarint(buf, 4611686018427387903ULL);
+
+ CU_ASSERT(8 == p - buf);
+
+ p = ngtcp2_get_uvarint(&n, buf);
+
+ CU_ASSERT(8 == p - buf);
+ CU_ASSERT(4611686018427387903ULL == n);
+
+ /* Check signed version */
+ s = 0;
+ p = ngtcp2_put_uvarint(buf, 4611686018427387903ULL);
+
+ CU_ASSERT(8 == p - buf);
+
+ p = ngtcp2_get_varint(&s, buf);
+
+ CU_ASSERT(8 == p - buf);
+ CU_ASSERT(4611686018427387903LL == s);
+}
+
+void test_ngtcp2_get_uvarintlen(void) {
+ uint8_t c;
+
+ c = 0x00;
+
+ CU_ASSERT(1 == ngtcp2_get_uvarintlen(&c));
+
+ c = 0x40;
+
+ CU_ASSERT(2 == ngtcp2_get_uvarintlen(&c));
+
+ c = 0x80;
+
+ CU_ASSERT(4 == ngtcp2_get_uvarintlen(&c));
+
+ c = 0xc0;
+
+ CU_ASSERT(8 == ngtcp2_get_uvarintlen(&c));
+}
+
+void test_ngtcp2_get_uint64(void) {
+ uint8_t buf[256];
+ const uint8_t *p;
+ uint64_t n;
+
+ /* 0 */
+ n = 1;
+ p = ngtcp2_put_uint64be(buf, 0);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint64(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(0 == n);
+
+ /* 12345678900 */
+ n = 0;
+ p = ngtcp2_put_uint64be(buf, 12345678900ULL);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint64(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(12345678900ULL == n);
+
+ /* 18446744073709551615 */
+ n = 0;
+ p = ngtcp2_put_uint64be(buf, 18446744073709551615ULL);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint64(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(18446744073709551615ULL == n);
+}
+
+void test_ngtcp2_get_uint48(void) {
+ uint8_t buf[256];
+ const uint8_t *p;
+ uint64_t n;
+
+ /* 0 */
+ n = 1;
+ p = ngtcp2_put_uint48be(buf, 0);
+
+ CU_ASSERT(6 == p - buf);
+
+ p = ngtcp2_get_uint48(&n, buf);
+
+ CU_ASSERT(6 == p - buf);
+ CU_ASSERT(0 == n);
+
+ /* 123456789 */
+ n = 0;
+ p = ngtcp2_put_uint48be(buf, 123456789);
+
+ CU_ASSERT(6 == p - buf);
+
+ p = ngtcp2_get_uint48(&n, buf);
+
+ CU_ASSERT(6 == p - buf);
+ CU_ASSERT(123456789 == n);
+
+ /* 281474976710655 */
+ n = 0;
+ p = ngtcp2_put_uint48be(buf, 281474976710655ULL);
+
+ CU_ASSERT(6 == p - buf);
+
+ p = ngtcp2_get_uint48(&n, buf);
+
+ CU_ASSERT(6 == p - buf);
+ CU_ASSERT(281474976710655ULL == n);
+}
+
+void test_ngtcp2_get_uint32(void) {
+ uint8_t buf[256];
+ const uint8_t *p;
+ uint32_t n;
+
+ /* 0 */
+ n = 1;
+ p = ngtcp2_put_uint32be(buf, 0);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint32(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(0 == n);
+
+ /* 123456 */
+ n = 0;
+ p = ngtcp2_put_uint32be(buf, 123456);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint32(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(123456 == n);
+
+ /* 4294967295 */
+ n = 0;
+ p = ngtcp2_put_uint32be(buf, 4294967295UL);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint32(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(4294967295UL == n);
+}
+
+void test_ngtcp2_get_uint24(void) {
+ uint8_t buf[256];
+ const uint8_t *p;
+ uint32_t n;
+
+ /* 0 */
+ n = 1;
+ p = ngtcp2_put_uint24be(buf, 0);
+
+ CU_ASSERT(3 == p - buf);
+
+ p = ngtcp2_get_uint24(&n, buf);
+
+ CU_ASSERT(3 == p - buf);
+ CU_ASSERT(0 == n);
+
+ /* 12345 */
+ n = 0;
+ p = ngtcp2_put_uint24be(buf, 12345);
+
+ CU_ASSERT(3 == p - buf);
+
+ p = ngtcp2_get_uint24(&n, buf);
+
+ CU_ASSERT(3 == p - buf);
+ CU_ASSERT(12345 == n);
+
+ /* 16777215 */
+ n = 0;
+ p = ngtcp2_put_uint24be(buf, 16777215);
+
+ CU_ASSERT(3 == p - buf);
+
+ p = ngtcp2_get_uint24(&n, buf);
+
+ CU_ASSERT(3 == p - buf);
+ CU_ASSERT(16777215 == n);
+}
+
+void test_ngtcp2_get_uint16(void) {
+ uint8_t buf[256];
+ const uint8_t *p;
+ uint16_t n;
+
+ /* 0 */
+ n = 1;
+ p = ngtcp2_put_uint16be(buf, 0);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint16(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(0 == n);
+
+ /* 1234 */
+ n = 0;
+ p = ngtcp2_put_uint16be(buf, 1234);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint16(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(1234 == n);
+
+ /* 65535 */
+ n = 0;
+ p = ngtcp2_put_uint16be(buf, 65535);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint16(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(65535 == n);
+}
+
+void test_ngtcp2_get_uint16be(void) {
+ uint8_t buf[256];
+ const uint8_t *p;
+ uint16_t n;
+
+ /* 0 */
+ n = 1;
+ p = ngtcp2_put_uint16(buf, 0);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint16be(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(0 == n);
+
+ /* 1234 */
+ n = 0;
+ p = ngtcp2_put_uint16(buf, ngtcp2_htons(1234));
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint16be(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(1234 == ngtcp2_ntohs(n));
+
+ /* 65535 */
+ n = 0;
+ p = ngtcp2_put_uint16(buf, 65535);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+
+ p = ngtcp2_get_uint16be(&n, buf);
+
+ CU_ASSERT(sizeof(n) == p - buf);
+ CU_ASSERT(65535 == n);
+}
+
+void test_ngtcp2_put_uvarintlen(void) {
+ CU_ASSERT(1 == ngtcp2_put_uvarintlen(0));
+ CU_ASSERT(1 == ngtcp2_put_uvarintlen(63));
+ CU_ASSERT(2 == ngtcp2_put_uvarintlen(64));
+ CU_ASSERT(2 == ngtcp2_put_uvarintlen(16383));
+ CU_ASSERT(4 == ngtcp2_put_uvarintlen(16384));
+ CU_ASSERT(4 == ngtcp2_put_uvarintlen(1073741823));
+ CU_ASSERT(8 == ngtcp2_put_uvarintlen(1073741824));
+ CU_ASSERT(8 == ngtcp2_put_uvarintlen(4611686018427387903ULL));
+}
+
+void test_ngtcp2_nth_server_bidi_id(void) {
+ CU_ASSERT(0 == ngtcp2_nth_server_bidi_id(0));
+ CU_ASSERT(1 == ngtcp2_nth_server_bidi_id(1));
+ CU_ASSERT(5 == ngtcp2_nth_server_bidi_id(2));
+ CU_ASSERT(9 == ngtcp2_nth_server_bidi_id(3));
+}
+
+void test_ngtcp2_nth_server_uni_id(void) {
+ CU_ASSERT(0 == ngtcp2_nth_server_uni_id(0));
+ CU_ASSERT(3 == ngtcp2_nth_server_uni_id(1));
+ CU_ASSERT(7 == ngtcp2_nth_server_uni_id(2));
+ CU_ASSERT(11 == ngtcp2_nth_server_uni_id(3));
+}
+
+void test_ngtcp2_nth_client_bidi_id(void) {
+ CU_ASSERT(0 == ngtcp2_nth_client_bidi_id(0));
+ CU_ASSERT(0 == ngtcp2_nth_client_bidi_id(1));
+ CU_ASSERT(4 == ngtcp2_nth_client_bidi_id(2));
+ CU_ASSERT(8 == ngtcp2_nth_client_bidi_id(3));
+}
+
+void test_ngtcp2_nth_client_uni_id(void) {
+ CU_ASSERT(0 == ngtcp2_nth_client_uni_id(0));
+ CU_ASSERT(2 == ngtcp2_nth_client_uni_id(1));
+ CU_ASSERT(6 == ngtcp2_nth_client_uni_id(2));
+ CU_ASSERT(10 == ngtcp2_nth_client_uni_id(3));
+}
diff --git a/tests/ngtcp2_conv_test.h b/tests/ngtcp2_conv_test.h
new file mode 100644
index 0000000..1cd4324
--- /dev/null
+++ b/tests/ngtcp2_conv_test.h
@@ -0,0 +1,46 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CONV_TEST_H
+#define NGTCP2_CONV_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_get_varint(void);
+void test_ngtcp2_get_uvarintlen(void);
+void test_ngtcp2_put_uvarintlen(void);
+void test_ngtcp2_get_uint64(void);
+void test_ngtcp2_get_uint48(void);
+void test_ngtcp2_get_uint32(void);
+void test_ngtcp2_get_uint24(void);
+void test_ngtcp2_get_uint16(void);
+void test_ngtcp2_get_uint16be(void);
+void test_ngtcp2_nth_server_bidi_id(void);
+void test_ngtcp2_nth_server_uni_id(void);
+void test_ngtcp2_nth_client_bidi_id(void);
+void test_ngtcp2_nth_client_uni_id(void);
+
+#endif /* NGTCP2_CONV_TEST_H */
diff --git a/tests/ngtcp2_crypto_test.c b/tests/ngtcp2_crypto_test.c
new file mode 100644
index 0000000..7469968
--- /dev/null
+++ b/tests/ngtcp2_crypto_test.c
@@ -0,0 +1,665 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_crypto_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_crypto.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_net.h"
+#include "ngtcp2_test_helper.h"
+
+static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t value) {
+ size_t valuelen = ngtcp2_put_uvarintlen(value);
+ return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(valuelen) + valuelen;
+}
+
+void test_ngtcp2_encode_transport_params(void) {
+ ngtcp2_transport_params params, nparams;
+ uint8_t buf[512];
+ ngtcp2_ssize nwrite;
+ int rv;
+ size_t i, len;
+ ngtcp2_cid rcid, scid, dcid;
+ uint8_t other_versions[sizeof(uint32_t) * 3];
+ ngtcp2_sockaddr_in6 *sa_in6;
+
+ rcid_init(&rcid);
+ scid_init(&scid);
+ dcid_init(&dcid);
+
+ memset(&params, 0, sizeof(params));
+ memset(&nparams, 0, sizeof(nparams));
+
+ for (i = 0; i < sizeof(other_versions); i += sizeof(uint32_t)) {
+ ngtcp2_put_uint32be(&other_versions[i], (uint32_t)(0xff000000u + i));
+ }
+
+ /* CH, required parameters only */
+ params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ params.ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
+ params.initial_scid = scid;
+
+ len = (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.initial_scid.datalen) +
+ params.initial_scid.datalen);
+
+ nwrite = ngtcp2_encode_transport_params(
+ buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, &params);
+
+ CU_ASSERT((ngtcp2_ssize)len == nwrite);
+
+ rv = ngtcp2_decode_transport_params(
+ &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, buf, (size_t)nwrite);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(params.initial_max_stream_data_bidi_local ==
+ nparams.initial_max_stream_data_bidi_local);
+ CU_ASSERT(params.initial_max_stream_data_bidi_remote ==
+ nparams.initial_max_stream_data_bidi_remote);
+ CU_ASSERT(params.initial_max_stream_data_uni ==
+ nparams.initial_max_stream_data_uni);
+ CU_ASSERT(params.initial_max_data == nparams.initial_max_data);
+ CU_ASSERT(params.initial_max_streams_bidi ==
+ nparams.initial_max_streams_bidi);
+ CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni);
+ CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout);
+ CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size);
+ CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent);
+ CU_ASSERT(params.stateless_reset_token_present ==
+ nparams.stateless_reset_token_present);
+ CU_ASSERT(params.disable_active_migration ==
+ nparams.disable_active_migration);
+ CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay);
+ CU_ASSERT(ngtcp2_cid_eq(&params.initial_scid, &nparams.initial_scid));
+
+ memset(&params, 0, sizeof(params));
+ memset(&nparams, 0, sizeof(nparams));
+
+ /* EE, required parameters only */
+ params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ params.ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
+ params.original_dcid = dcid;
+ params.initial_scid = scid;
+
+ len = (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.original_dcid.datalen) +
+ params.original_dcid.datalen) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.initial_scid.datalen) +
+ params.initial_scid.datalen);
+
+ nwrite = ngtcp2_encode_transport_params(
+ buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
+ &params);
+
+ CU_ASSERT((ngtcp2_ssize)len == nwrite);
+
+ rv = ngtcp2_decode_transport_params(
+ &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf,
+ (size_t)nwrite);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(params.initial_max_stream_data_bidi_local ==
+ nparams.initial_max_stream_data_bidi_local);
+ CU_ASSERT(params.initial_max_stream_data_bidi_remote ==
+ nparams.initial_max_stream_data_bidi_remote);
+ CU_ASSERT(params.initial_max_stream_data_uni ==
+ nparams.initial_max_stream_data_uni);
+ CU_ASSERT(params.initial_max_data == nparams.initial_max_data);
+ CU_ASSERT(params.initial_max_streams_bidi ==
+ nparams.initial_max_streams_bidi);
+ CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni);
+ CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout);
+ CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size);
+ CU_ASSERT(params.stateless_reset_token_present ==
+ nparams.stateless_reset_token_present);
+ CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent);
+ CU_ASSERT(params.disable_active_migration ==
+ nparams.disable_active_migration);
+ CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay);
+ CU_ASSERT(ngtcp2_cid_eq(&params.original_dcid, &nparams.original_dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&params.initial_scid, &nparams.initial_scid));
+ CU_ASSERT(params.retry_scid_present == nparams.retry_scid_present);
+
+ memset(&params, 0, sizeof(params));
+ memset(&nparams, 0, sizeof(nparams));
+
+ /* CH, all parameters */
+ params.initial_max_stream_data_bidi_local = 1000000007;
+ params.initial_max_stream_data_bidi_remote = 961748941;
+ params.initial_max_stream_data_uni = 982451653;
+ params.initial_max_data = 1000000009;
+ params.initial_max_streams_bidi = 909;
+ params.initial_max_streams_uni = 911;
+ params.max_idle_timeout = 1023 * NGTCP2_MILLISECONDS;
+ params.max_udp_payload_size = 1400;
+ params.ack_delay_exponent = 20;
+ params.disable_active_migration = 1;
+ params.max_ack_delay = 59 * NGTCP2_MILLISECONDS;
+ params.initial_scid = scid;
+ params.active_connection_id_limit = 1000000007;
+ params.max_datagram_frame_size = 65535;
+ params.grease_quic_bit = 1;
+ params.version_info.chosen_version = NGTCP2_PROTO_VER_V1;
+ params.version_info.other_versions = other_versions;
+ params.version_info.other_versionslen = sizeof(other_versions);
+ params.version_info_present = 1;
+
+ len =
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ params.initial_max_stream_data_bidi_local) +
+ varint_paramlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ params.initial_max_stream_data_bidi_remote) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI,
+ params.initial_max_stream_data_uni) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA,
+ params.initial_max_data) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI,
+ params.initial_max_streams_bidi) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
+ params.initial_max_streams_uni) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT,
+ params.max_idle_timeout / NGTCP2_MILLISECONDS) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
+ params.max_udp_payload_size) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT,
+ params.ack_delay_exponent) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) +
+ ngtcp2_put_uvarintlen(0)) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY,
+ params.max_ack_delay / NGTCP2_MILLISECONDS) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT,
+ params.active_connection_id_limit) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.initial_scid.datalen) +
+ params.initial_scid.datalen) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
+ params.max_datagram_frame_size) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) +
+ ngtcp2_put_uvarintlen(0)) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) +
+ ngtcp2_put_uvarintlen(sizeof(params.version_info.chosen_version) +
+ params.version_info.other_versionslen) +
+ sizeof(params.version_info.chosen_version) +
+ params.version_info.other_versionslen);
+
+ nwrite = ngtcp2_encode_transport_params(
+ NULL, 0, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, &params);
+
+ CU_ASSERT((ngtcp2_ssize)len == nwrite);
+
+ for (i = 0; i < len; ++i) {
+ nwrite = ngtcp2_encode_transport_params(
+ buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, &params);
+ CU_ASSERT(NGTCP2_ERR_NOBUF == nwrite);
+ }
+ nwrite = ngtcp2_encode_transport_params(
+ buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, &params);
+
+ CU_ASSERT((ngtcp2_ssize)i == nwrite);
+
+ rv = ngtcp2_decode_transport_params(
+ &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, buf, (size_t)nwrite);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(params.initial_max_stream_data_bidi_local ==
+ nparams.initial_max_stream_data_bidi_local);
+ CU_ASSERT(params.initial_max_stream_data_bidi_remote ==
+ nparams.initial_max_stream_data_bidi_remote);
+ CU_ASSERT(params.initial_max_stream_data_uni ==
+ nparams.initial_max_stream_data_uni);
+ CU_ASSERT(params.initial_max_data == nparams.initial_max_data);
+ CU_ASSERT(params.initial_max_streams_bidi ==
+ nparams.initial_max_streams_bidi);
+ CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni);
+ CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout);
+ CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size);
+ CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent);
+ CU_ASSERT(params.disable_active_migration ==
+ nparams.disable_active_migration);
+ CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay);
+ CU_ASSERT(params.active_connection_id_limit ==
+ nparams.active_connection_id_limit);
+ CU_ASSERT(params.max_datagram_frame_size == nparams.max_datagram_frame_size);
+ CU_ASSERT(params.grease_quic_bit == nparams.grease_quic_bit);
+ CU_ASSERT(params.version_info_present == nparams.version_info_present);
+ CU_ASSERT(params.version_info.chosen_version ==
+ nparams.version_info.chosen_version);
+ CU_ASSERT(0 == memcmp(params.version_info.other_versions,
+ nparams.version_info.other_versions,
+ params.version_info.other_versionslen));
+
+ memset(&params, 0, sizeof(params));
+ memset(&nparams, 0, sizeof(nparams));
+
+ /* EE, all parameters */
+ params.initial_max_stream_data_bidi_local = 1000000007;
+ params.initial_max_stream_data_bidi_remote = 961748941;
+ params.initial_max_stream_data_uni = 982451653;
+ params.initial_max_data = 1000000009;
+ params.initial_max_streams_bidi = 908;
+ params.initial_max_streams_uni = 16383;
+ params.max_idle_timeout = 16363 * NGTCP2_MILLISECONDS;
+ params.max_udp_payload_size = 1200;
+ params.stateless_reset_token_present = 1;
+ memset(params.stateless_reset_token, 0xf1,
+ sizeof(params.stateless_reset_token));
+ params.ack_delay_exponent = 20;
+ params.preferred_address_present = 1;
+ params.preferred_address.ipv4_present = 0;
+ sa_in6 = &params.preferred_address.ipv6;
+ sa_in6->sin6_family = AF_INET6;
+ memset(&sa_in6->sin6_addr, 0xe1, sizeof(sa_in6->sin6_addr));
+ sa_in6->sin6_port = ngtcp2_htons(63111);
+ params.preferred_address.ipv6_present = 1;
+ scid_init(&params.preferred_address.cid);
+ memset(params.preferred_address.stateless_reset_token, 0xd1,
+ sizeof(params.preferred_address.stateless_reset_token));
+ params.disable_active_migration = 1;
+ params.max_ack_delay = 63 * NGTCP2_MILLISECONDS;
+ params.retry_scid_present = 1;
+ params.retry_scid = rcid;
+ params.original_dcid = dcid;
+ params.initial_scid = scid;
+ params.active_connection_id_limit = 1073741824;
+ params.max_datagram_frame_size = 63;
+ params.grease_quic_bit = 1;
+ params.version_info.chosen_version = NGTCP2_PROTO_VER_V1;
+ params.version_info.other_versions = other_versions;
+ params.version_info.other_versionslen = ngtcp2_arraylen(other_versions);
+ params.version_info_present = 1;
+
+ len =
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ params.initial_max_stream_data_bidi_local) +
+ varint_paramlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ params.initial_max_stream_data_bidi_remote) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI,
+ params.initial_max_stream_data_uni) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA,
+ params.initial_max_data) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI,
+ params.initial_max_streams_bidi) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
+ params.initial_max_streams_uni) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT,
+ params.max_idle_timeout / NGTCP2_MILLISECONDS) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
+ params.max_udp_payload_size) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT,
+ params.ack_delay_exponent) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) +
+ ngtcp2_put_uvarintlen(0)) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY,
+ params.max_ack_delay / NGTCP2_MILLISECONDS) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT,
+ params.active_connection_id_limit) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) +
+ ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) +
+ NGTCP2_STATELESS_RESET_TOKENLEN) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) +
+ ngtcp2_put_uvarintlen(4 + 2 + 16 + 2 + 1 +
+ params.preferred_address.cid.datalen +
+ NGTCP2_STATELESS_RESET_TOKENLEN) +
+ 4 + 2 + 16 + 2 + 1 + params.preferred_address.cid.datalen +
+ NGTCP2_STATELESS_RESET_TOKENLEN) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.retry_scid.datalen) +
+ params.retry_scid.datalen) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.original_dcid.datalen) +
+ params.original_dcid.datalen) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.initial_scid.datalen) +
+ params.initial_scid.datalen) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
+ params.max_datagram_frame_size) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) +
+ ngtcp2_put_uvarintlen(0)) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) +
+ ngtcp2_put_uvarintlen(sizeof(params.version_info.chosen_version) +
+ params.version_info.other_versionslen) +
+ sizeof(params.version_info.chosen_version) +
+ params.version_info.other_versionslen);
+
+ nwrite = ngtcp2_encode_transport_params(
+ NULL, 0, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, &params);
+
+ CU_ASSERT((ngtcp2_ssize)len == nwrite);
+
+ for (i = 0; i < len; ++i) {
+ nwrite = ngtcp2_encode_transport_params(
+ buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, &params);
+
+ CU_ASSERT(NGTCP2_ERR_NOBUF == nwrite);
+ }
+ nwrite = ngtcp2_encode_transport_params(
+ buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, &params);
+
+ CU_ASSERT((ngtcp2_ssize)i == nwrite);
+
+ rv = ngtcp2_decode_transport_params(
+ &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf,
+ (size_t)nwrite);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(params.initial_max_stream_data_bidi_local ==
+ nparams.initial_max_stream_data_bidi_local);
+ CU_ASSERT(params.initial_max_stream_data_bidi_remote ==
+ nparams.initial_max_stream_data_bidi_remote);
+ CU_ASSERT(params.initial_max_stream_data_uni ==
+ nparams.initial_max_stream_data_uni);
+ CU_ASSERT(params.initial_max_data == nparams.initial_max_data);
+ CU_ASSERT(params.initial_max_streams_bidi ==
+ nparams.initial_max_streams_bidi);
+ CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni);
+ CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout);
+ CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size);
+ CU_ASSERT(0 == memcmp(params.stateless_reset_token,
+ nparams.stateless_reset_token,
+ sizeof(params.stateless_reset_token)));
+ CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent);
+ CU_ASSERT(params.preferred_address_present ==
+ nparams.preferred_address_present);
+ CU_ASSERT(0 == memcmp(&params.preferred_address.ipv4,
+ &nparams.preferred_address.ipv4,
+ sizeof(params.preferred_address.ipv4)));
+ CU_ASSERT(params.preferred_address.ipv4_present ==
+ nparams.preferred_address.ipv4_present);
+ CU_ASSERT(0 == memcmp(&params.preferred_address.ipv6,
+ &nparams.preferred_address.ipv6,
+ sizeof(params.preferred_address.ipv6)));
+ CU_ASSERT(params.preferred_address.ipv6_present ==
+ nparams.preferred_address.ipv6_present);
+ CU_ASSERT(ngtcp2_cid_eq(&params.preferred_address.cid,
+ &nparams.preferred_address.cid));
+ CU_ASSERT(0 ==
+ memcmp(params.preferred_address.stateless_reset_token,
+ nparams.preferred_address.stateless_reset_token,
+ sizeof(params.preferred_address.stateless_reset_token)));
+ CU_ASSERT(params.disable_active_migration ==
+ nparams.disable_active_migration);
+ CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay);
+ CU_ASSERT(params.retry_scid_present == nparams.retry_scid_present);
+ CU_ASSERT(ngtcp2_cid_eq(&params.retry_scid, &nparams.retry_scid));
+ CU_ASSERT(ngtcp2_cid_eq(&params.initial_scid, &nparams.initial_scid));
+ CU_ASSERT(ngtcp2_cid_eq(&params.original_dcid, &nparams.original_dcid));
+ CU_ASSERT(params.active_connection_id_limit ==
+ nparams.active_connection_id_limit);
+ CU_ASSERT(params.max_datagram_frame_size == nparams.max_datagram_frame_size);
+ CU_ASSERT(params.grease_quic_bit = nparams.grease_quic_bit);
+ CU_ASSERT(params.version_info_present == nparams.version_info_present);
+ CU_ASSERT(params.version_info.chosen_version ==
+ nparams.version_info.chosen_version);
+ CU_ASSERT(0 == memcmp(params.version_info.other_versions,
+ nparams.version_info.other_versions,
+ params.version_info.other_versionslen));
+}
+
+void test_ngtcp2_decode_transport_params_new(void) {
+ ngtcp2_transport_params params, *nparams;
+ uint8_t buf[512];
+ ngtcp2_ssize nwrite;
+ int rv;
+ size_t i, len;
+ ngtcp2_cid rcid, scid, dcid;
+ uint8_t other_versions[sizeof(uint32_t) * 3];
+ ngtcp2_sockaddr_in *sa_in;
+
+ rcid_init(&rcid);
+ scid_init(&scid);
+ dcid_init(&dcid);
+
+ memset(&params, 0, sizeof(params));
+ memset(&nparams, 0, sizeof(nparams));
+
+ for (i = 0; i < sizeof(other_versions); i += sizeof(uint32_t)) {
+ ngtcp2_put_uint32be(&other_versions[i], (uint32_t)(0xff000000u + i));
+ }
+
+ /* EE, required parameters only */
+ params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ params.ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
+ params.original_dcid = dcid;
+ params.initial_scid = scid;
+
+ len = (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.original_dcid.datalen) +
+ params.original_dcid.datalen) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.initial_scid.datalen) +
+ params.initial_scid.datalen);
+
+ nwrite = ngtcp2_encode_transport_params(
+ buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
+ &params);
+
+ CU_ASSERT((ngtcp2_ssize)len == nwrite);
+
+ rv = ngtcp2_decode_transport_params_new(
+ &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf,
+ (size_t)nwrite, NULL);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(params.initial_max_stream_data_bidi_local ==
+ nparams->initial_max_stream_data_bidi_local);
+ CU_ASSERT(params.initial_max_stream_data_bidi_remote ==
+ nparams->initial_max_stream_data_bidi_remote);
+ CU_ASSERT(params.initial_max_stream_data_uni ==
+ nparams->initial_max_stream_data_uni);
+ CU_ASSERT(params.initial_max_data == nparams->initial_max_data);
+ CU_ASSERT(params.initial_max_streams_bidi ==
+ nparams->initial_max_streams_bidi);
+ CU_ASSERT(params.initial_max_streams_uni == nparams->initial_max_streams_uni);
+ CU_ASSERT(params.max_idle_timeout == nparams->max_idle_timeout);
+ CU_ASSERT(params.max_udp_payload_size == nparams->max_udp_payload_size);
+ CU_ASSERT(params.stateless_reset_token_present ==
+ nparams->stateless_reset_token_present);
+ CU_ASSERT(params.ack_delay_exponent == nparams->ack_delay_exponent);
+ CU_ASSERT(params.disable_active_migration ==
+ nparams->disable_active_migration);
+ CU_ASSERT(params.max_ack_delay == nparams->max_ack_delay);
+ CU_ASSERT(ngtcp2_cid_eq(&params.original_dcid, &nparams->original_dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&params.initial_scid, &nparams->initial_scid));
+ CU_ASSERT(params.retry_scid_present == nparams->retry_scid_present);
+
+ ngtcp2_transport_params_del(nparams, NULL);
+ memset(&params, 0, sizeof(params));
+
+ /* EE, all parameters */
+ params.initial_max_stream_data_bidi_local = 1000000007;
+ params.initial_max_stream_data_bidi_remote = 961748941;
+ params.initial_max_stream_data_uni = 982451653;
+ params.initial_max_data = 1000000009;
+ params.initial_max_streams_bidi = 908;
+ params.initial_max_streams_uni = 16383;
+ params.max_idle_timeout = 16363 * NGTCP2_MILLISECONDS;
+ params.max_udp_payload_size = 1200;
+ params.stateless_reset_token_present = 1;
+ memset(params.stateless_reset_token, 0xf1,
+ sizeof(params.stateless_reset_token));
+ params.ack_delay_exponent = 20;
+ params.preferred_address_present = 1;
+ sa_in = &params.preferred_address.ipv4;
+ sa_in->sin_family = AF_INET;
+ memset(&sa_in->sin_addr, 0xf1, sizeof(sa_in->sin_addr));
+ sa_in->sin_port = ngtcp2_htons(11732);
+ params.preferred_address.ipv4_present = 1;
+ params.preferred_address.ipv6_present = 0;
+ scid_init(&params.preferred_address.cid);
+ memset(params.preferred_address.stateless_reset_token, 0xd1,
+ sizeof(params.preferred_address.stateless_reset_token));
+ params.disable_active_migration = 1;
+ params.max_ack_delay = 63 * NGTCP2_MILLISECONDS;
+ params.retry_scid_present = 1;
+ params.retry_scid = rcid;
+ params.original_dcid = dcid;
+ params.initial_scid = scid;
+ params.active_connection_id_limit = 1073741824;
+ params.max_datagram_frame_size = 63;
+ params.grease_quic_bit = 1;
+ params.version_info.chosen_version = NGTCP2_PROTO_VER_V1;
+ params.version_info.other_versions = other_versions;
+ params.version_info.other_versionslen = ngtcp2_arraylen(other_versions);
+ params.version_info_present = 1;
+
+ len =
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ params.initial_max_stream_data_bidi_local) +
+ varint_paramlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ params.initial_max_stream_data_bidi_remote) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI,
+ params.initial_max_stream_data_uni) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA,
+ params.initial_max_data) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI,
+ params.initial_max_streams_bidi) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
+ params.initial_max_streams_uni) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT,
+ params.max_idle_timeout / NGTCP2_MILLISECONDS) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
+ params.max_udp_payload_size) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT,
+ params.ack_delay_exponent) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) +
+ ngtcp2_put_uvarintlen(0)) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY,
+ params.max_ack_delay / NGTCP2_MILLISECONDS) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT,
+ params.active_connection_id_limit) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) +
+ ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) +
+ NGTCP2_STATELESS_RESET_TOKENLEN) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) +
+ ngtcp2_put_uvarintlen(4 + 2 + 16 + 2 + 1 +
+ params.preferred_address.cid.datalen +
+ NGTCP2_STATELESS_RESET_TOKENLEN) +
+ 4 + 2 + 16 + 2 + 1 + params.preferred_address.cid.datalen +
+ NGTCP2_STATELESS_RESET_TOKENLEN) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.retry_scid.datalen) +
+ params.retry_scid.datalen) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.original_dcid.datalen) +
+ params.original_dcid.datalen) +
+ (ngtcp2_put_uvarintlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) +
+ ngtcp2_put_uvarintlen(params.initial_scid.datalen) +
+ params.initial_scid.datalen) +
+ varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
+ params.max_datagram_frame_size) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) +
+ ngtcp2_put_uvarintlen(0)) +
+ (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) +
+ ngtcp2_put_uvarintlen(sizeof(params.version_info.chosen_version) +
+ params.version_info.other_versionslen) +
+ sizeof(params.version_info.chosen_version) +
+ params.version_info.other_versionslen);
+
+ nwrite = ngtcp2_encode_transport_params(
+ buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
+ &params);
+
+ CU_ASSERT((ngtcp2_ssize)len == nwrite);
+
+ rv = ngtcp2_decode_transport_params_new(
+ &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf,
+ (size_t)nwrite, NULL);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(params.initial_max_stream_data_bidi_local ==
+ nparams->initial_max_stream_data_bidi_local);
+ CU_ASSERT(params.initial_max_stream_data_bidi_remote ==
+ nparams->initial_max_stream_data_bidi_remote);
+ CU_ASSERT(params.initial_max_stream_data_uni ==
+ nparams->initial_max_stream_data_uni);
+ CU_ASSERT(params.initial_max_data == nparams->initial_max_data);
+ CU_ASSERT(params.initial_max_streams_bidi ==
+ nparams->initial_max_streams_bidi);
+ CU_ASSERT(params.initial_max_streams_uni == nparams->initial_max_streams_uni);
+ CU_ASSERT(params.max_idle_timeout == nparams->max_idle_timeout);
+ CU_ASSERT(params.max_udp_payload_size == nparams->max_udp_payload_size);
+ CU_ASSERT(0 == memcmp(params.stateless_reset_token,
+ nparams->stateless_reset_token,
+ sizeof(params.stateless_reset_token)));
+ CU_ASSERT(params.ack_delay_exponent == nparams->ack_delay_exponent);
+ CU_ASSERT(params.preferred_address_present ==
+ nparams->preferred_address_present);
+ CU_ASSERT(0 == memcmp(&params.preferred_address.ipv4,
+ &nparams->preferred_address.ipv4,
+ sizeof(params.preferred_address.ipv4)));
+ CU_ASSERT(params.preferred_address.ipv4_present ==
+ nparams->preferred_address.ipv4_present);
+ CU_ASSERT(0 == memcmp(&params.preferred_address.ipv6,
+ &nparams->preferred_address.ipv6,
+ sizeof(params.preferred_address.ipv6)));
+ CU_ASSERT(params.preferred_address.ipv6_present ==
+ nparams->preferred_address.ipv6_present);
+ CU_ASSERT(ngtcp2_cid_eq(&params.preferred_address.cid,
+ &nparams->preferred_address.cid));
+ CU_ASSERT(0 ==
+ memcmp(params.preferred_address.stateless_reset_token,
+ nparams->preferred_address.stateless_reset_token,
+ sizeof(params.preferred_address.stateless_reset_token)));
+ CU_ASSERT(params.disable_active_migration ==
+ nparams->disable_active_migration);
+ CU_ASSERT(params.max_ack_delay == nparams->max_ack_delay);
+ CU_ASSERT(params.retry_scid_present == nparams->retry_scid_present);
+ CU_ASSERT(ngtcp2_cid_eq(&params.retry_scid, &nparams->retry_scid));
+ CU_ASSERT(ngtcp2_cid_eq(&params.initial_scid, &nparams->initial_scid));
+ CU_ASSERT(ngtcp2_cid_eq(&params.original_dcid, &nparams->original_dcid));
+ CU_ASSERT(params.active_connection_id_limit ==
+ nparams->active_connection_id_limit);
+ CU_ASSERT(params.max_datagram_frame_size == nparams->max_datagram_frame_size);
+ CU_ASSERT(params.grease_quic_bit = nparams->grease_quic_bit);
+ CU_ASSERT(params.version_info_present == nparams->version_info_present);
+ CU_ASSERT(params.version_info.chosen_version ==
+ nparams->version_info.chosen_version);
+ CU_ASSERT(0 == memcmp(params.version_info.other_versions,
+ nparams->version_info.other_versions,
+ params.version_info.other_versionslen));
+
+ ngtcp2_transport_params_del(nparams, NULL);
+}
diff --git a/tests/ngtcp2_crypto_test.h b/tests/ngtcp2_crypto_test.h
new file mode 100644
index 0000000..e57c4bc
--- /dev/null
+++ b/tests/ngtcp2_crypto_test.h
@@ -0,0 +1,35 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_TEST_H
+#define NGTCP2_CRYPTO_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_encode_transport_params(void);
+void test_ngtcp2_decode_transport_params_new(void);
+
+#endif /* NGTCP2_CRYPTO_TEST_H */
diff --git a/tests/ngtcp2_gaptr_test.c b/tests/ngtcp2_gaptr_test.c
new file mode 100644
index 0000000..5612b56
--- /dev/null
+++ b/tests/ngtcp2_gaptr_test.c
@@ -0,0 +1,127 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_gaptr_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_gaptr.h"
+#include "ngtcp2_test_helper.h"
+#include "ngtcp2_mem.h"
+
+void test_ngtcp2_gaptr_push(void) {
+ ngtcp2_gaptr gaptr;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_ksl_it it;
+ ngtcp2_range r;
+ int rv;
+ size_t i;
+
+ ngtcp2_gaptr_init(&gaptr, mem);
+
+ rv = ngtcp2_gaptr_push(&gaptr, 0, 1);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&gaptr.gap);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(1 == r.begin);
+ CU_ASSERT(UINT64_MAX == r.end);
+
+ rv = ngtcp2_gaptr_push(&gaptr, 12389, 133);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&gaptr.gap);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(1 == r.begin);
+ CU_ASSERT(12389 == r.end);
+
+ ngtcp2_ksl_it_next(&it);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(12389 + 133 == r.begin);
+ CU_ASSERT(UINT64_MAX == r.end);
+
+ for (i = 0; i < 2; ++i) {
+ rv = ngtcp2_gaptr_push(&gaptr, 1, 12389);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&gaptr.gap);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(12389 + 133 == r.begin);
+ CU_ASSERT(UINT64_MAX == r.end);
+ }
+
+ rv = ngtcp2_gaptr_push(&gaptr, 12389 + 133 - 1, 2);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&gaptr.gap);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(12389 + 133 + 1 == r.begin);
+ CU_ASSERT(UINT64_MAX == r.end);
+
+ ngtcp2_gaptr_free(&gaptr);
+}
+
+void test_ngtcp2_gaptr_is_pushed(void) {
+ ngtcp2_gaptr gaptr;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ int rv;
+
+ ngtcp2_gaptr_init(&gaptr, mem);
+
+ rv = ngtcp2_gaptr_push(&gaptr, 1000000007, 1009);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_gaptr_is_pushed(&gaptr, 1000000007, 1009));
+ CU_ASSERT(!ngtcp2_gaptr_is_pushed(&gaptr, 1000000007, 1010));
+
+ ngtcp2_gaptr_free(&gaptr);
+}
+
+void test_ngtcp2_gaptr_drop_first_gap(void) {
+ ngtcp2_gaptr gaptr;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ int rv;
+
+ ngtcp2_gaptr_init(&gaptr, mem);
+
+ rv = ngtcp2_gaptr_push(&gaptr, 113245, 12);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_gaptr_drop_first_gap(&gaptr);
+
+ CU_ASSERT(ngtcp2_gaptr_is_pushed(&gaptr, 0, 1));
+ CU_ASSERT(113245 + 12 == ngtcp2_gaptr_first_gap_offset(&gaptr));
+
+ ngtcp2_gaptr_free(&gaptr);
+}
diff --git a/tests/ngtcp2_gaptr_test.h b/tests/ngtcp2_gaptr_test.h
new file mode 100644
index 0000000..cc6507c
--- /dev/null
+++ b/tests/ngtcp2_gaptr_test.h
@@ -0,0 +1,36 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_GAPTR_TEST_H
+#define NGTCP2_GAPTR_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_gaptr_push(void);
+void test_ngtcp2_gaptr_is_pushed(void);
+void test_ngtcp2_gaptr_drop_first_gap(void);
+
+#endif /* NGTCP2_GAPTR_TEST_H */
diff --git a/tests/ngtcp2_idtr_test.c b/tests/ngtcp2_idtr_test.c
new file mode 100644
index 0000000..90d15ba
--- /dev/null
+++ b/tests/ngtcp2_idtr_test.c
@@ -0,0 +1,79 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_idtr_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_idtr.h"
+#include "ngtcp2_test_helper.h"
+#include "ngtcp2_mem.h"
+
+static int64_t stream_id_from_id(uint64_t id) { return (int64_t)(id * 4); }
+
+void test_ngtcp2_idtr_open(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_idtr idtr;
+ int rv;
+ ngtcp2_ksl_it it;
+ ngtcp2_range key;
+
+ ngtcp2_idtr_init(&idtr, 0, mem);
+
+ rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(0));
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&idtr.gap.gap);
+ key = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(1 == key.begin);
+ CU_ASSERT(UINT64_MAX == key.end);
+
+ rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(1000000007));
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&idtr.gap.gap);
+ key = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(1 == key.begin);
+ CU_ASSERT(1000000007 == key.end);
+
+ ngtcp2_ksl_it_next(&it);
+ key = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(1000000008 == key.begin);
+ CU_ASSERT(UINT64_MAX == key.end);
+
+ rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(0));
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_IN_USE == rv);
+
+ rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(1000000007));
+
+ CU_ASSERT(NGTCP2_ERR_STREAM_IN_USE == rv);
+
+ ngtcp2_idtr_free(&idtr);
+}
diff --git a/tests/ngtcp2_idtr_test.h b/tests/ngtcp2_idtr_test.h
new file mode 100644
index 0000000..ebba934
--- /dev/null
+++ b/tests/ngtcp2_idtr_test.h
@@ -0,0 +1,34 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_IDTR_TEST_H
+#define NGTCP2_IDTR_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_idtr_open(void);
+
+#endif /* NGTCP2_IDTR_TEST_H */
diff --git a/tests/ngtcp2_ksl_test.c b/tests/ngtcp2_ksl_test.c
new file mode 100644
index 0000000..54bdd1b
--- /dev/null
+++ b/tests/ngtcp2_ksl_test.c
@@ -0,0 +1,502 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ksl_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_test_helper.h"
+
+static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs < *(int64_t *)rhs;
+}
+
+void test_ngtcp2_ksl_insert(void) {
+ static const int64_t keys[] = {10, 3, 8, 11, 16, 12, 1, 5, 4,
+ 0, 13, 7, 9, 2, 14, 6, 15};
+ ngtcp2_ksl ksl;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+ ngtcp2_ksl_it it;
+ int64_t k;
+
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL));
+ it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]);
+
+ CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it));
+ }
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &keys[i]));
+ it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ CU_ASSERT(keys[i] < *(int64_t *)ngtcp2_ksl_it_key(&it));
+ }
+ }
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* check the case that the right end range is removed */
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ for (i = 0; i < 32; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+ }
+
+ /* Removing 15 which is the last node in a blk. */
+ k = 15;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, &it, &k));
+
+ CU_ASSERT(16 == *(int64_t *)ngtcp2_ksl_it_key(&it));
+
+ /* Insert 15 again works */
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+
+ k = 15;
+ it = ngtcp2_ksl_lower_bound(&ksl, &k);
+
+ CU_ASSERT(15 == *(int64_t *)ngtcp2_ksl_it_key(&it));
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* Check the case that the intermediate node contains smaller key
+ than ancestor node. Make sure that inserting key larger than
+ that still works.*/
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ for (i = 0; i < 760; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+ }
+
+ k = 255;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ k = 254;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ k = 253;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+
+ k = 253;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+ it = ngtcp2_ksl_lower_bound(&ksl, &k);
+
+ CU_ASSERT(253 == *(int64_t *)ngtcp2_ksl_it_key(&it));
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* check merge node (head) */
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ for (i = 0; i < 32; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+ }
+
+ /* Removing these 3 nodes kicks merging 2 nodes under head */
+ k = 15;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ k = 14;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ k = 13;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+
+ CU_ASSERT(29 == ksl.head->n);
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* check merge node (non head) */
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ for (i = 0; i < 32 + 18; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+ }
+
+ /* Removing these 3 nodes kicks merging 2 nodes */
+ k = 15;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ k = 14;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ k = 13;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+
+ CU_ASSERT(2 == ksl.head->n);
+ CU_ASSERT(29 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 0)->blk->n);
+ CU_ASSERT(18 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 1)->blk->n);
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* Iterate backwards */
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ /* split nodes */
+ for (i = 0; i < 100; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+ }
+
+ /* merge nodes */
+ for (i = 0; i < 50; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ }
+
+ i = 99;
+ for (it = ngtcp2_ksl_end(&ksl); !ngtcp2_ksl_it_begin(&it);) {
+ ngtcp2_ksl_it_prev(&it);
+
+ CU_ASSERT((int64_t)i-- == *(int64_t *)ngtcp2_ksl_it_key(&it));
+ }
+
+ /* head only */
+ for (i = 50; i < 88; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k));
+ }
+
+ i = 99;
+ for (it = ngtcp2_ksl_end(&ksl); !ngtcp2_ksl_it_begin(&it);) {
+ ngtcp2_ksl_it_prev(&it);
+
+ CU_ASSERT((int64_t)i-- == *(int64_t *)ngtcp2_ksl_it_key(&it));
+ }
+
+ ngtcp2_ksl_free(&ksl);
+}
+
+void test_ngtcp2_ksl_clear(void) {
+ ngtcp2_ksl ksl;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_ksl_it it;
+ size_t i;
+ int64_t k;
+
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ for (i = 0; i < 100; ++i) {
+ k = (int64_t)i;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL));
+ }
+
+ ngtcp2_ksl_clear(&ksl);
+
+ CU_ASSERT(0 == ngtcp2_ksl_len(&ksl));
+
+ it = ngtcp2_ksl_begin(&ksl);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ it = ngtcp2_ksl_end(&ksl);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_ksl_free(&ksl);
+}
+
+void test_ngtcp2_ksl_range(void) {
+ static const ngtcp2_range keys[] = {
+ {10, 11}, {3, 4}, {8, 9}, {11, 12}, {16, 17}, {12, 13},
+ {1, 2}, {5, 6}, {4, 5}, {0, 1}, {13, 14}, {7, 8},
+ {9, 10}, {2, 3}, {14, 15}, {6, 7}, {15, 16}, {17, 18},
+ {18, 19}, {19, 20}, {20, 21}, {202, 203}, {203, 204}, {204, 205},
+ {205, 206}, {206, 207}, {207, 208}, {208, 209}, {209, 210}, {210, 211},
+ {211, 212}, {212, 213}, {213, 214}, {214, 215}, {215, 216}};
+ ngtcp2_ksl ksl;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+ ngtcp2_range r;
+ ngtcp2_ksl_it it;
+ ngtcp2_ksl_node *node;
+
+ ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem);
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL));
+ it = ngtcp2_ksl_lower_bound_compar(&ksl, &keys[i],
+ ngtcp2_ksl_range_exclusive_compar);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(ngtcp2_range_eq(&keys[i], &r));
+ }
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &keys[i]));
+ it = ngtcp2_ksl_lower_bound_compar(&ksl, &keys[i],
+ ngtcp2_ksl_range_exclusive_compar);
+
+ if (!ngtcp2_ksl_it_end(&it)) {
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(keys[i].end <= r.begin);
+ }
+ }
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* check merge node (head) */
+ ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem);
+
+ for (i = 0; i < 32; ++i) {
+ ngtcp2_range_init(&r, i, i + 1);
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL));
+ }
+
+ /* Removing these 3 nodes kicks merging 2 nodes under head */
+ ngtcp2_range_init(&r, 13, 14);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ ngtcp2_range_init(&r, 14, 15);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ ngtcp2_range_init(&r, 15, 16);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ CU_ASSERT(29 == ksl.head->n);
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* check merge node (non head) */
+ ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem);
+
+ for (i = 0; i < 32 + 18; ++i) {
+ ngtcp2_range_init(&r, i, i + 1);
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL));
+ }
+
+ /* Removing these 3 nodes kicks merging 2 nodes */
+ ngtcp2_range_init(&r, 13, 14);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ ngtcp2_range_init(&r, 14, 15);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ ngtcp2_range_init(&r, 15, 16);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ CU_ASSERT(2 == ksl.head->n);
+ CU_ASSERT(29 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 0)->blk->n);
+ CU_ASSERT(18 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 1)->blk->n);
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* shift_left */
+ ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem);
+
+ for (i = 1; i < 6400; i += 100) {
+ ngtcp2_range_init(&r, i, i + 1);
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL));
+ }
+
+ ngtcp2_range_init(&r, 1501, 1502);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+ ngtcp2_range_init(&r, 1401, 1402);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ r = *(ngtcp2_range *)(void *)ngtcp2_ksl_nth_node(
+ &ksl, ngtcp2_ksl_nth_node(&ksl, ksl.head, 1)->blk, 0)
+ ->key;
+
+ CU_ASSERT(1701 == r.begin);
+
+ ngtcp2_ksl_free(&ksl);
+
+ /* shift_right */
+ ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem);
+
+ for (i = 0; i < 32; ++i) {
+ ngtcp2_range_init(&r, i, i + 1);
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL));
+ }
+
+ ngtcp2_range_init(&r, 17, 18);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+ ngtcp2_range_init(&r, 16, 17);
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r));
+
+ node = ngtcp2_ksl_nth_node(&ksl, ksl.head, 0);
+ r = *(ngtcp2_range *)(void *)ngtcp2_ksl_nth_node(&ksl, node->blk,
+ node->blk->n - 1)
+ ->key;
+
+ CU_ASSERT(14 == r.begin);
+
+ ngtcp2_ksl_free(&ksl);
+}
+
+void test_ngtcp2_ksl_update_key_range(void) {
+ static ngtcp2_range ranges[] = {
+ {0, 5}, {10, 15}, {20, 25}, {30, 35}, {40, 45}, {50, 55},
+ {60, 65}, {70, 75}, {80, 85}, {90, 95}, {100, 105}, {110, 115},
+ {120, 125}, {130, 135}, {140, 145}, {150, 155}, {160, 165}, {170, 175}};
+ ngtcp2_ksl ksl;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+ ngtcp2_range r;
+ ngtcp2_ksl_it it;
+
+ ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem);
+
+ for (i = 0; i < ngtcp2_arraylen(ranges); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &ranges[i], NULL));
+ }
+
+ r.begin = 70;
+ r.end = 72;
+ ngtcp2_ksl_update_key(&ksl, &ranges[7], &r);
+
+ r.begin = 73;
+ r.end = 74;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL));
+
+ r.begin = 74;
+ r.end = 75;
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL));
+
+ r.begin = 74;
+ r.end = 75;
+ it = ngtcp2_ksl_lower_bound_compar(&ksl, &r,
+ ngtcp2_ksl_range_exclusive_compar);
+
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ CU_ASSERT(r.begin == 74);
+ CU_ASSERT(r.end == 75);
+
+ ngtcp2_ksl_free(&ksl);
+}
+
+static void shuffle(int64_t *a, size_t n) {
+ size_t i, j;
+ int64_t t;
+
+ for (i = n - 1; i >= 1; --i) {
+ j = (size_t)((double)(i + 1) * rand() / (RAND_MAX + 1.0));
+ t = a[j];
+ a[j] = a[i];
+ a[i] = t;
+ }
+}
+
+void test_ngtcp2_ksl_dup(void) {
+ static int64_t keys[16000];
+ size_t i, j;
+ ngtcp2_ksl ksl;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_ksl_it it;
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ keys[i] = (int64_t)i;
+ }
+
+ for (j = 0; j < 10; ++j) {
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ shuffle(keys, ngtcp2_arraylen(keys));
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL));
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT ==
+ ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL));
+
+ it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]);
+
+ CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it));
+ }
+
+ shuffle(keys, ngtcp2_arraylen(keys));
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &keys[i]));
+
+ it = ngtcp2_ksl_begin(&ksl);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT ==
+ ngtcp2_ksl_remove(&ksl, &it, &keys[i]));
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it) ||
+ keys[i] < *(int64_t *)ngtcp2_ksl_it_key(&it));
+
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL));
+
+ it = ngtcp2_ksl_begin(&ksl);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT ==
+ ngtcp2_ksl_insert(&ksl, &it, &keys[i], NULL));
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]);
+ CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it));
+ }
+
+ ngtcp2_ksl_free(&ksl);
+ }
+}
+
+void test_ngtcp2_ksl_remove_hint(void) {
+ static int64_t keys[16000];
+ ngtcp2_ksl ksl;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_ksl_it it;
+ size_t i, j;
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ keys[i] = (int64_t)i;
+ }
+
+ for (j = 0; j < 10; ++j) {
+ ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem);
+
+ shuffle(keys, ngtcp2_arraylen(keys));
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL));
+ }
+
+ shuffle(keys, ngtcp2_arraylen(keys));
+
+ for (i = 0; i < ngtcp2_arraylen(keys); ++i) {
+ it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]);
+
+ CU_ASSERT(!ngtcp2_ksl_it_end(&it));
+ CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it));
+ CU_ASSERT(0 == ngtcp2_ksl_remove_hint(&ksl, &it, &it, &keys[i]));
+
+ it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it) ||
+ keys[i] != *(int64_t *)ngtcp2_ksl_it_key(&it));
+ CU_ASSERT(ngtcp2_arraylen(keys) - i - 1 == ngtcp2_ksl_len(&ksl));
+ }
+
+ ngtcp2_ksl_free(&ksl);
+ }
+}
diff --git a/tests/ngtcp2_ksl_test.h b/tests/ngtcp2_ksl_test.h
new file mode 100644
index 0000000..9cdc4d4
--- /dev/null
+++ b/tests/ngtcp2_ksl_test.h
@@ -0,0 +1,39 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_KSL_TEST_H
+#define NGTCP2_KSL_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_ksl_insert(void);
+void test_ngtcp2_ksl_clear(void);
+void test_ngtcp2_ksl_range(void);
+void test_ngtcp2_ksl_update_key_range(void);
+void test_ngtcp2_ksl_dup(void);
+void test_ngtcp2_ksl_remove_hint(void);
+
+#endif /* NGTCP2_KSL_TEST_H */
diff --git a/tests/ngtcp2_map_test.c b/tests/ngtcp2_map_test.c
new file mode 100644
index 0000000..8ee4086
--- /dev/null
+++ b/tests/ngtcp2_map_test.c
@@ -0,0 +1,206 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_map_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_map.h"
+
+typedef struct strentry {
+ ngtcp2_map_key_type key;
+ const char *str;
+} strentry;
+
+static void strentry_init(strentry *entry, ngtcp2_map_key_type key,
+ const char *str) {
+ entry->key = key;
+ entry->str = str;
+}
+
+void test_ngtcp2_map(void) {
+ strentry foo, FOO, bar, baz, shrubbery;
+ ngtcp2_map map;
+ ngtcp2_map_init(&map, ngtcp2_mem_default());
+
+ strentry_init(&foo, 1, "foo");
+ strentry_init(&FOO, 1, "FOO");
+ strentry_init(&bar, 2, "bar");
+ strentry_init(&baz, 3, "baz");
+ strentry_init(&shrubbery, 4, "shrubbery");
+
+ CU_ASSERT(0 == ngtcp2_map_insert(&map, foo.key, &foo));
+ CU_ASSERT(strcmp("foo", ((strentry *)ngtcp2_map_find(&map, 1))->str) == 0);
+ CU_ASSERT(1 == ngtcp2_map_size(&map));
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT ==
+ ngtcp2_map_insert(&map, FOO.key, &FOO));
+
+ CU_ASSERT(1 == ngtcp2_map_size(&map));
+ CU_ASSERT(strcmp("foo", ((strentry *)ngtcp2_map_find(&map, 1))->str) == 0);
+
+ CU_ASSERT(0 == ngtcp2_map_insert(&map, bar.key, &bar));
+ CU_ASSERT(2 == ngtcp2_map_size(&map));
+
+ CU_ASSERT(0 == ngtcp2_map_insert(&map, baz.key, &baz));
+ CU_ASSERT(3 == ngtcp2_map_size(&map));
+
+ CU_ASSERT(0 == ngtcp2_map_insert(&map, shrubbery.key, &shrubbery));
+ CU_ASSERT(4 == ngtcp2_map_size(&map));
+
+ CU_ASSERT(strcmp("baz", ((strentry *)ngtcp2_map_find(&map, 3))->str) == 0);
+
+ ngtcp2_map_remove(&map, 3);
+ CU_ASSERT(3 == ngtcp2_map_size(&map));
+ CU_ASSERT(NULL == ngtcp2_map_find(&map, 3));
+
+ ngtcp2_map_remove(&map, 1);
+ CU_ASSERT(2 == ngtcp2_map_size(&map));
+ CU_ASSERT(NULL == ngtcp2_map_find(&map, 1));
+
+ /* Erasing non-existent entry */
+ ngtcp2_map_remove(&map, 1);
+ CU_ASSERT(2 == ngtcp2_map_size(&map));
+ CU_ASSERT(NULL == ngtcp2_map_find(&map, 1));
+
+ CU_ASSERT(strcmp("bar", ((strentry *)ngtcp2_map_find(&map, 2))->str) == 0);
+ CU_ASSERT(strcmp("shrubbery", ((strentry *)ngtcp2_map_find(&map, 4))->str) ==
+ 0);
+
+ ngtcp2_map_free(&map);
+}
+
+static void shuffle(int *a, int n) {
+ int i;
+ for (i = n - 1; i >= 1; --i) {
+ size_t j = (size_t)((double)(i + 1) * rand() / (RAND_MAX + 1.0));
+ int t = a[j];
+ a[j] = a[i];
+ a[i] = t;
+ }
+}
+
+static int eachfun(void *data, void *ptr) {
+ (void)data;
+ (void)ptr;
+
+ return 0;
+}
+
+#define NUM_ENT 6000
+static strentry arr[NUM_ENT];
+static int order[NUM_ENT];
+
+void test_ngtcp2_map_functional(void) {
+ ngtcp2_map map;
+ int i;
+ strentry *ent;
+
+ ngtcp2_map_init(&map, ngtcp2_mem_default());
+ for (i = 0; i < NUM_ENT; ++i) {
+ strentry_init(&arr[i], (ngtcp2_map_key_type)(i + 1), "foo");
+ order[i] = i + 1;
+ }
+ /* insertion */
+ shuffle(order, NUM_ENT);
+ for (i = 0; i < NUM_ENT; ++i) {
+ ent = &arr[order[i] - 1];
+ CU_ASSERT(0 == ngtcp2_map_insert(&map, ent->key, ent));
+ }
+
+ CU_ASSERT(NUM_ENT == ngtcp2_map_size(&map));
+
+ /* traverse */
+ ngtcp2_map_each(&map, eachfun, NULL);
+ /* find */
+ shuffle(order, NUM_ENT);
+ for (i = 0; i < NUM_ENT; ++i) {
+ CU_ASSERT(NULL != ngtcp2_map_find(&map, (ngtcp2_map_key_type)order[i]));
+ }
+ /* remove */
+ for (i = 0; i < NUM_ENT; ++i) {
+ CU_ASSERT(0 == ngtcp2_map_remove(&map, (ngtcp2_map_key_type)order[i]));
+ }
+
+ /* each_free (but no op function for testing purpose) */
+ for (i = 0; i < NUM_ENT; ++i) {
+ strentry_init(&arr[i], (ngtcp2_map_key_type)(i + 1), "foo");
+ }
+ /* insert once again */
+ for (i = 0; i < NUM_ENT; ++i) {
+ ent = &arr[i];
+ CU_ASSERT(0 == ngtcp2_map_insert(&map, ent->key, ent));
+ }
+ ngtcp2_map_each_free(&map, eachfun, NULL);
+ ngtcp2_map_free(&map);
+}
+
+static int entry_free(void *data, void *ptr) {
+ const ngtcp2_mem *mem = ptr;
+
+ mem->free(data, NULL);
+ return 0;
+}
+
+void test_ngtcp2_map_each_free(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ strentry *foo = mem->malloc(sizeof(strentry), NULL),
+ *bar = mem->malloc(sizeof(strentry), NULL),
+ *baz = mem->malloc(sizeof(strentry), NULL),
+ *shrubbery = mem->malloc(sizeof(strentry), NULL);
+ ngtcp2_map map;
+ ngtcp2_map_init(&map, ngtcp2_mem_default());
+
+ strentry_init(foo, 1, "foo");
+ strentry_init(bar, 2, "bar");
+ strentry_init(baz, 3, "baz");
+ strentry_init(shrubbery, 4, "shrubbery");
+
+ ngtcp2_map_insert(&map, foo->key, foo);
+ ngtcp2_map_insert(&map, bar->key, bar);
+ ngtcp2_map_insert(&map, baz->key, baz);
+ ngtcp2_map_insert(&map, shrubbery->key, shrubbery);
+
+ ngtcp2_map_each_free(&map, entry_free, (void *)mem);
+ ngtcp2_map_free(&map);
+}
+
+void test_ngtcp2_map_clear(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_map map;
+ strentry foo;
+
+ strentry_init(&foo, 1, "foo");
+
+ ngtcp2_map_init(&map, mem);
+
+ CU_ASSERT(0 == ngtcp2_map_insert(&map, foo.key, &foo));
+
+ ngtcp2_map_clear(&map);
+
+ CU_ASSERT(0 == ngtcp2_map_size(&map));
+
+ ngtcp2_map_free(&map);
+}
diff --git a/tests/ngtcp2_map_test.h b/tests/ngtcp2_map_test.h
new file mode 100644
index 0000000..c8dd3eb
--- /dev/null
+++ b/tests/ngtcp2_map_test.h
@@ -0,0 +1,38 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_MAP_TEST_H
+#define NGTCP2_MAP_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_map(void);
+void test_ngtcp2_map_functional(void);
+void test_ngtcp2_map_each_free(void);
+void test_ngtcp2_map_clear(void);
+
+#endif /* NGTCP2_MAP_TEST_H */
diff --git a/tests/ngtcp2_pkt_test.c b/tests/ngtcp2_pkt_test.c
new file mode 100644
index 0000000..f225610
--- /dev/null
+++ b/tests/ngtcp2_pkt_test.c
@@ -0,0 +1,1644 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pkt_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_test_helper.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_str.h"
+#include "ngtcp2_vec.h"
+
+static int null_retry_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)dest;
+ (void)aead;
+ (void)aead_ctx;
+ (void)plaintext;
+ (void)plaintextlen;
+ (void)nonce;
+ (void)noncelen;
+ (void)aad;
+ (void)aadlen;
+
+ if (plaintextlen && plaintext != dest) {
+ memcpy(dest, plaintext, plaintextlen);
+ }
+ memset(dest + plaintextlen, 0, NGTCP2_RETRY_TAGLEN);
+
+ return 0;
+}
+
+void test_ngtcp2_pkt_decode_version_cid(void) {
+ uint8_t buf[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
+ ngtcp2_version_cid vc;
+ int rv;
+ uint8_t *p;
+
+ /* Supported QUIC version */
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN);
+ *p++ = NGTCP2_MAX_CIDLEN - 1;
+ p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_PROTO_VER_V1 == vc.version);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen);
+ CU_ASSERT(&buf[6] == vc.dcid);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN - 1 == vc.scidlen);
+ CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1] == vc.scid);
+
+ /* Unsupported QUIC version */
+ memset(buf, 0, sizeof(buf));
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, 0xffffff00);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN);
+ *p++ = NGTCP2_MAX_CIDLEN - 1;
+ ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, sizeof(buf), 0);
+
+ CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION == rv);
+ CU_ASSERT(0xffffff00 == vc.version);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen);
+ CU_ASSERT(&buf[6] == vc.dcid);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN - 1 == vc.scidlen);
+ CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1] == vc.scid);
+
+ /* Unsupported QUIC version with UDP payload size < 1200 */
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, 0xffffff00);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN);
+ *p++ = NGTCP2_MAX_CIDLEN - 1;
+ p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+
+ /* Supported QUIC version with long CID */
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1);
+ *p++ = NGTCP2_MAX_CIDLEN + 1;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN + 1);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+
+ /* Unsupported QUIC version with long CID */
+ memset(buf, 0, sizeof(buf));
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, 0xffffff00);
+ *p++ = NGTCP2_MAX_CIDLEN + 1;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN + 1);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, sizeof(buf), 0);
+
+ CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION == rv);
+ CU_ASSERT(0xffffff00 == vc.version);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN + 1 == vc.dcidlen);
+ CU_ASSERT(&buf[6] == vc.dcid);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.scidlen);
+ CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1 + 1] == vc.scid);
+
+ /* VN */
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, 0);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN);
+ *p++ = NGTCP2_MAX_CIDLEN - 1;
+ p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == vc.version);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen);
+ CU_ASSERT(&buf[6] == vc.dcid);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN - 1 == vc.scidlen);
+ CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1] == vc.scid);
+
+ /* VN with long CID */
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, 0);
+ *p++ = NGTCP2_MAX_CIDLEN + 1;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN + 1);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == vc.version);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN + 1 == vc.dcidlen);
+ CU_ASSERT(&buf[6] == vc.dcid);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.scidlen);
+ CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1 + 1] == vc.scid);
+
+ /* Malformed Long packet */
+ p = buf;
+ *p++ = NGTCP2_HEADER_FORM_BIT;
+ p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1);
+ *p++ = NGTCP2_MAX_CIDLEN;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN);
+ *p++ = NGTCP2_MAX_CIDLEN - 1;
+ p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1);
+ --p;
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+
+ /* Short packet */
+ p = buf;
+ *p++ = 0;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN);
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf),
+ NGTCP2_MAX_CIDLEN);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == vc.version);
+ CU_ASSERT(&buf[1] == vc.dcid);
+ CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen);
+ CU_ASSERT(NULL == vc.scid);
+ CU_ASSERT(0 == vc.scidlen);
+
+ /* Malformed Short packet */
+ p = buf;
+ *p++ = 0;
+ p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN);
+ --p;
+
+ rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf),
+ NGTCP2_MAX_CIDLEN);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+}
+
+void test_ngtcp2_pkt_decode_hd_long(void) {
+ ngtcp2_pkt_hd hd, nhd;
+ uint8_t buf[256];
+ ngtcp2_ssize rv;
+ ngtcp2_cid dcid, scid;
+ size_t len;
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ /* Handshake */
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE,
+ &dcid, &scid, 0xe1e2e3e4u, 4, NGTCP2_PROTO_VER_V1, 16383);
+
+ rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd);
+
+ len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen + NGTCP2_PKT_LENGTHLEN + 4;
+
+ CU_ASSERT((ngtcp2_ssize)len == rv);
+ CU_ASSERT(buf[0] & NGTCP2_FIXED_BIT_MASK);
+
+ rv = pkt_decode_hd_long(&nhd, buf, len);
+
+ CU_ASSERT((ngtcp2_ssize)len == rv);
+ CU_ASSERT(hd.type == nhd.type);
+ CU_ASSERT(hd.flags == nhd.flags);
+ CU_ASSERT(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid));
+ CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num);
+ CU_ASSERT(hd.version == nhd.version);
+ CU_ASSERT(hd.len == nhd.len);
+
+ /* Handshake without Fixed Bit set */
+ ngtcp2_pkt_hd_init(
+ &hd, NGTCP2_PKT_FLAG_LONG_FORM | NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR,
+ NGTCP2_PKT_HANDSHAKE, &dcid, &scid, 0xe1e2e3e4u, 4, NGTCP2_PROTO_VER_V1,
+ 16383);
+
+ rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd);
+
+ len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen + NGTCP2_PKT_LENGTHLEN + 4;
+
+ CU_ASSERT((ngtcp2_ssize)len == rv);
+ CU_ASSERT((buf[0] & NGTCP2_FIXED_BIT_MASK) == 0);
+
+ rv = pkt_decode_hd_long(&nhd, buf, len);
+
+ CU_ASSERT((ngtcp2_ssize)len == rv);
+ CU_ASSERT(hd.type == nhd.type);
+ CU_ASSERT(hd.flags == nhd.flags);
+ CU_ASSERT(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid));
+ CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num);
+ CU_ASSERT(hd.version == nhd.version);
+ CU_ASSERT(hd.len == nhd.len);
+
+ /* VN */
+ /* Set random packet type */
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE,
+ &dcid, &scid, 0, 4, NGTCP2_PROTO_VER_V1, 0);
+
+ rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd);
+ /* Set version field to 0 */
+ memset(&buf[1], 0, 4);
+
+ len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen;
+
+ CU_ASSERT((ngtcp2_ssize)len == rv - NGTCP2_PKT_LENGTHLEN - 4 /* pkt_num */);
+
+ rv = pkt_decode_hd_long(&nhd, buf, len);
+
+ CU_ASSERT((ngtcp2_ssize)len == rv);
+ CU_ASSERT(NGTCP2_PKT_VERSION_NEGOTIATION == nhd.type);
+ CU_ASSERT((hd.flags & ~NGTCP2_PKT_FLAG_LONG_FORM) == nhd.flags);
+ CU_ASSERT(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid));
+ CU_ASSERT(hd.pkt_num == nhd.pkt_num);
+ CU_ASSERT(0 == nhd.version);
+ CU_ASSERT(hd.len == nhd.len);
+}
+
+void test_ngtcp2_pkt_decode_hd_short(void) {
+ ngtcp2_pkt_hd hd, nhd;
+ uint8_t buf[256];
+ ngtcp2_ssize rv;
+ size_t expectedlen;
+ ngtcp2_cid dcid, zcid;
+
+ dcid_init(&dcid);
+ ngtcp2_cid_zero(&zcid);
+
+ /* 4 bytes packet number */
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
+ 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0);
+
+ expectedlen = 1 + dcid.datalen + 4;
+
+ rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(buf[0] & NGTCP2_FIXED_BIT_MASK);
+
+ rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(hd.flags == nhd.flags);
+ CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_empty(&nhd.scid));
+ CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num);
+ CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen);
+ CU_ASSERT(0 == nhd.version);
+ CU_ASSERT(0 == nhd.len);
+
+ /* 4 bytes packet number without Fixed Bit set */
+ ngtcp2_pkt_hd_init(
+ &hd, NGTCP2_PKT_FLAG_NONE | NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR,
+ NGTCP2_PKT_1RTT, &dcid, NULL, 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0);
+
+ expectedlen = 1 + dcid.datalen + 4;
+
+ rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT((buf[0] & NGTCP2_FIXED_BIT_MASK) == 0);
+
+ rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(hd.flags == nhd.flags);
+ CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_empty(&nhd.scid));
+ CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num);
+ CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen);
+ CU_ASSERT(0 == nhd.version);
+ CU_ASSERT(0 == nhd.len);
+
+ /* 2 bytes packet number */
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
+ 0xe1e2e3e4u, 2, 0xd1d2d3d4u, 0);
+
+ expectedlen = 1 + dcid.datalen + 2;
+
+ rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+
+ rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(hd.flags == nhd.flags);
+ CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_empty(&nhd.scid));
+ CU_ASSERT(0xe3e4u == nhd.pkt_num);
+ CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen);
+ CU_ASSERT(0 == nhd.version);
+ CU_ASSERT(0 == nhd.len);
+
+ /* 1 byte packet number */
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
+ 0xe1e2e3e4u, 1, 0xd1d2d3d4u, 0);
+
+ expectedlen = 1 + dcid.datalen + 1;
+
+ rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+
+ rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(hd.flags == nhd.flags);
+ CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_empty(&nhd.scid));
+ CU_ASSERT(0xe4 == nhd.pkt_num);
+ CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen);
+ CU_ASSERT(0 == nhd.version);
+ CU_ASSERT(0 == nhd.len);
+
+ /* With Key Phase */
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_KEY_PHASE, NGTCP2_PKT_1RTT, &dcid,
+ NULL, 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0);
+
+ expectedlen = 1 + dcid.datalen + 4;
+
+ rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+
+ rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ /* key phase bit is protected by header protection and
+ ngtcp2_pkt_decode_hd_short does not decode it. */
+ CU_ASSERT(NGTCP2_PKT_FLAG_NONE == nhd.flags);
+ CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_empty(&nhd.scid));
+ CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num);
+ CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen);
+ CU_ASSERT(0 == nhd.version);
+ CU_ASSERT(0 == nhd.len);
+
+ /* With empty DCID */
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, NULL, NULL,
+ 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0);
+
+ expectedlen = 1 + 4;
+
+ rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+
+ rv = pkt_decode_hd_short(&nhd, buf, expectedlen, 0);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(hd.flags == nhd.flags);
+ CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type);
+ CU_ASSERT(ngtcp2_cid_empty(&nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_empty(&nhd.scid));
+ CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num);
+ CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen);
+ CU_ASSERT(0 == nhd.version);
+ CU_ASSERT(0 == nhd.len);
+}
+
+void test_ngtcp2_pkt_decode_stream_frame(void) {
+ uint8_t buf[256];
+ size_t buflen;
+ ngtcp2_frame fr;
+ ngtcp2_ssize rv;
+ size_t expectedlen;
+
+ /* 32 bits Stream ID + 62 bits Offset + Data Length */
+ buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_LEN_BIT, 0xf1f2f3f4u,
+ 0x31f2f3f4f5f6f7f8llu, 0x14);
+
+ expectedlen = 1 + 8 + 8 + 1 + 20;
+
+ CU_ASSERT(expectedlen == buflen);
+
+ rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(0 == fr.stream.fin);
+ CU_ASSERT(0xf1f2f3f4u == fr.stream.stream_id);
+ CU_ASSERT(0x31f2f3f4f5f6f7f8llu == fr.stream.offset);
+ CU_ASSERT(1 == fr.stream.datacnt);
+ CU_ASSERT(0x14 == fr.stream.data[0].len);
+
+ /* Cutting 1 bytes from the tail must cause invalid argument
+ error */
+ rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen - 1);
+
+ CU_ASSERT(NGTCP2_ERR_FRAME_ENCODING == rv);
+
+ memset(&fr, 0, sizeof(fr));
+
+ /* 6 bits Stream ID + no Offset + Data Length */
+ buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_LEN_BIT, 0x31, 0x00,
+ 0x14);
+
+ expectedlen = 1 + 1 + 0 + 1 + 20;
+
+ CU_ASSERT(expectedlen == buflen);
+
+ rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(0 == fr.stream.fin);
+ CU_ASSERT(0x31 == fr.stream.stream_id);
+ CU_ASSERT(0x00 == fr.stream.offset);
+ CU_ASSERT(1 == fr.stream.datacnt);
+ CU_ASSERT(0x14 == fr.stream.data[0].len);
+
+ /* Cutting 1 bytes from the tail must cause invalid argument
+ error */
+ rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen - 1);
+
+ CU_ASSERT(NGTCP2_ERR_FRAME_ENCODING == rv);
+
+ memset(&fr, 0, sizeof(fr));
+
+ /* Fin bit set + no Data Length */
+ buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_FIN_BIT, 0x31f2f3f4u,
+ 0x00, 0x14);
+
+ expectedlen = 1 + 4 + 20;
+
+ CU_ASSERT(expectedlen == buflen);
+
+ rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(1 == fr.stream.fin);
+ CU_ASSERT(0x31f2f3f4u == fr.stream.stream_id);
+ CU_ASSERT(0x00 == fr.stream.offset);
+ CU_ASSERT(1 == fr.stream.datacnt);
+ CU_ASSERT(0x14 == fr.stream.data[0].len);
+
+ memset(&fr, 0, sizeof(fr));
+}
+
+void test_ngtcp2_pkt_decode_ack_frame(void) {
+ uint8_t buf[256];
+ size_t buflen;
+ ngtcp2_frame fr;
+ ngtcp2_ssize rv;
+ size_t expectedlen;
+
+ /* 62 bits Largest Acknowledged */
+ buflen = ngtcp2_t_encode_ack_frame(buf, 0x31f2f3f4f5f6f7f8llu,
+ 0x31e2e3e4e5e6e7e8llu, 99,
+ 0x31d2d3d4d5d6d7d8llu);
+
+ expectedlen = 1 + 8 + 1 + 1 + 8 + 2 + 8;
+
+ CU_ASSERT(expectedlen == buflen);
+
+ rv = ngtcp2_pkt_decode_ack_frame(&fr.ack, buf, buflen);
+
+ CU_ASSERT((ngtcp2_ssize)expectedlen == rv);
+ CU_ASSERT(0x31f2f3f4f5f6f7f8llu == fr.ack.largest_ack);
+ CU_ASSERT(1 == fr.ack.rangecnt);
+ CU_ASSERT(0x31e2e3e4e5e6e7e8llu == fr.ack.first_ack_range);
+ CU_ASSERT(99 == fr.ack.ranges[0].gap);
+ CU_ASSERT(0x31d2d3d4d5d6d7d8llu == fr.ack.ranges[0].len);
+}
+
+void test_ngtcp2_pkt_decode_padding_frame(void) {
+ uint8_t buf[256];
+ ngtcp2_frame fr;
+ ngtcp2_ssize rv;
+ size_t paddinglen = 31;
+
+ memset(buf, 0, paddinglen);
+ buf[paddinglen] = NGTCP2_FRAME_STREAM;
+
+ rv = ngtcp2_pkt_decode_padding_frame(&fr.padding, buf, paddinglen + 1);
+
+ CU_ASSERT((ngtcp2_ssize)paddinglen == rv);
+ CU_ASSERT((size_t)31 == fr.padding.len);
+}
+
+void test_ngtcp2_pkt_encode_stream_frame(void) {
+ const uint8_t data[] = "0123456789abcdef0";
+ uint8_t buf[256];
+ ngtcp2_frame fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+ size_t i;
+
+ /* 32 bits Stream ID + 62 bits Offset + Data Length */
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.fin = 0;
+ fr.stream.stream_id = 0xf1f2f3f4u;
+ fr.stream.offset = 0x31f2f3f4f5f6f7f8llu;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = strsize(data);
+ fr.stream.data[0].base = (uint8_t *)data;
+
+ framelen = 1 + 8 + 8 + 1 + 17;
+
+ rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr.stream);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT((NGTCP2_STREAM_OFF_BIT | NGTCP2_STREAM_LEN_BIT) ==
+ nfr.stream.flags);
+ CU_ASSERT(fr.stream.fin == nfr.stream.fin);
+ CU_ASSERT(fr.stream.stream_id == nfr.stream.stream_id);
+ CU_ASSERT(fr.stream.offset == nfr.stream.offset);
+ CU_ASSERT(1 == nfr.stream.datacnt);
+ CU_ASSERT(fr.stream.data[0].len == nfr.stream.data[0].len);
+ CU_ASSERT(0 == memcmp(fr.stream.data[0].base, nfr.stream.data[0].base,
+ fr.stream.data[0].len));
+
+ memset(&nfr, 0, sizeof(nfr));
+
+ /* 6 bits Stream ID + No Offset + Data Length */
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.fin = 0;
+ fr.stream.stream_id = 0x31;
+ fr.stream.offset = 0;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = strsize(data);
+ fr.stream.data[0].base = (uint8_t *)data;
+
+ framelen = 1 + 1 + 1 + 17;
+
+ rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr.stream);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(NGTCP2_STREAM_LEN_BIT == nfr.stream.flags);
+ CU_ASSERT(fr.stream.fin == nfr.stream.fin);
+ CU_ASSERT(fr.stream.stream_id == nfr.stream.stream_id);
+ CU_ASSERT(fr.stream.offset == nfr.stream.offset);
+ CU_ASSERT(1 == nfr.stream.datacnt);
+ CU_ASSERT(fr.stream.data[0].len == nfr.stream.data[0].len);
+ CU_ASSERT(0 == memcmp(fr.stream.data[0].base, nfr.stream.data[0].base,
+ fr.stream.data[0].len));
+
+ memset(&nfr, 0, sizeof(nfr));
+
+ /* Fin + 32 bits Stream ID + 62 bits Offset + Data Length */
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.fin = 1;
+ fr.stream.stream_id = 0xf1f2f3f4u;
+ fr.stream.offset = 0x31f2f3f4f5f6f7f8llu;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = strsize(data);
+ fr.stream.data[0].base = (uint8_t *)data;
+
+ framelen = 1 + 8 + 8 + 1 + 17;
+
+ rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr.stream);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT((NGTCP2_STREAM_FIN_BIT | NGTCP2_STREAM_OFF_BIT |
+ NGTCP2_STREAM_LEN_BIT) == nfr.stream.flags);
+ CU_ASSERT(fr.stream.fin == nfr.stream.fin);
+ CU_ASSERT(fr.stream.stream_id == nfr.stream.stream_id);
+ CU_ASSERT(fr.stream.offset == nfr.stream.offset);
+ CU_ASSERT(1 == nfr.stream.datacnt);
+ CU_ASSERT(fr.stream.data[0].len == nfr.stream.data[0].len);
+ CU_ASSERT(0 == memcmp(fr.stream.data[0].base, nfr.stream.data[0].base,
+ fr.stream.data[0].len));
+
+ /* Make sure that we check the length properly. */
+ for (i = 1; i < framelen; ++i) {
+ rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, i);
+
+ CU_ASSERT(NGTCP2_ERR_FRAME_ENCODING == rv);
+ }
+
+ memset(&nfr, 0, sizeof(nfr));
+
+ /* NOBUF: Fin + 32 bits Stream ID + 62 bits Offset + Data Length */
+ fr.type = NGTCP2_FRAME_STREAM;
+ fr.stream.fin = 1;
+ fr.stream.stream_id = 0xf1f2f3f4u;
+ fr.stream.offset = 0x31f2f3f4f5f6f7f8llu;
+ fr.stream.datacnt = 1;
+ fr.stream.data[0].len = strsize(data);
+ fr.stream.data[0].base = (uint8_t *)data;
+
+ framelen = 1 + 8 + 8 + 1 + 17;
+
+ rv = ngtcp2_pkt_encode_stream_frame(buf, framelen - 1, &fr.stream);
+
+ CU_ASSERT(NGTCP2_ERR_NOBUF == rv);
+}
+
+void test_ngtcp2_pkt_encode_ack_frame(void) {
+ uint8_t buf[256];
+ ngtcp2_max_frame mfr, nmfr;
+ ngtcp2_frame *fr = &mfr.fr, *nfr = &nmfr.fr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+ size_t i;
+ ngtcp2_ack_range *ranges;
+
+ /* 0 Num Blocks */
+ fr->type = NGTCP2_FRAME_ACK;
+ fr->ack.largest_ack = 0xf1f2f3f4llu;
+ fr->ack.first_ack_range = 0;
+ fr->ack.ack_delay = 0;
+ fr->ack.rangecnt = 0;
+
+ framelen = 1 + 8 + 1 + 1 + 1;
+
+ rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr->type == nfr->type);
+ CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack);
+ CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay);
+ CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt);
+
+ memset(&nmfr, 0, sizeof(nmfr));
+
+ /* 2 Num Blocks */
+ fr->type = NGTCP2_FRAME_ACK;
+ fr->ack.largest_ack = 0xf1f2f3f4llu;
+ fr->ack.first_ack_range = 0xe1e2e3e4llu;
+ fr->ack.ack_delay = 0xf1f2;
+ fr->ack.rangecnt = 2;
+ ranges = fr->ack.ranges;
+ ranges[0].gap = 255;
+ ranges[0].len = 0xd1d2d3d4llu;
+ ranges[1].gap = 1;
+ ranges[1].len = 0xd1d2d3d4llu;
+
+ framelen = 1 + 8 + 4 + 1 + 8 + (2 + 8) + (1 + 8);
+
+ rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr->type == nfr->type);
+ CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack);
+ CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay);
+ CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt);
+
+ for (i = 0; i < fr->ack.rangecnt; ++i) {
+ CU_ASSERT(fr->ack.ranges[i].gap == nfr->ack.ranges[i].gap);
+ CU_ASSERT(fr->ack.ranges[i].len == nfr->ack.ranges[i].len);
+ }
+
+ memset(&nmfr, 0, sizeof(nmfr));
+}
+
+void test_ngtcp2_pkt_encode_ack_ecn_frame(void) {
+ uint8_t buf[256];
+ ngtcp2_max_frame mfr, nmfr;
+ ngtcp2_frame *fr = &mfr.fr, *nfr = &nmfr.fr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+ size_t i;
+ ngtcp2_ack_range *ranges;
+
+ /* 0 Num Blocks */
+ fr->type = NGTCP2_FRAME_ACK_ECN;
+ fr->ack.largest_ack = 0xf1f2f3f4llu;
+ fr->ack.first_ack_range = 0;
+ fr->ack.ack_delay = 0;
+ fr->ack.rangecnt = 0;
+ fr->ack.ecn.ect0 = 64;
+ fr->ack.ecn.ect1 = 16384;
+ fr->ack.ecn.ce = 1073741824;
+
+ framelen = 1 + 8 + 1 + 1 + 1 + 2 + 4 + 8;
+
+ rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr->type == nfr->type);
+ CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack);
+ CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay);
+ CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt);
+ CU_ASSERT(fr->ack.ecn.ect0 == nfr->ack.ecn.ect0);
+ CU_ASSERT(fr->ack.ecn.ect1 == nfr->ack.ecn.ect1);
+ CU_ASSERT(fr->ack.ecn.ce == nfr->ack.ecn.ce);
+
+ memset(&nmfr, 0, sizeof(nmfr));
+
+ /* 2 Num Blocks */
+ fr->type = NGTCP2_FRAME_ACK_ECN;
+ fr->ack.largest_ack = 0xf1f2f3f4llu;
+ fr->ack.first_ack_range = 0xe1e2e3e4llu;
+ fr->ack.ack_delay = 0xf1f2;
+ fr->ack.rangecnt = 2;
+ ranges = fr->ack.ranges;
+ ranges[0].gap = 255;
+ ranges[0].len = 0xd1d2d3d4llu;
+ ranges[1].gap = 1;
+ ranges[1].len = 0xd1d2d3d4llu;
+ fr->ack.ecn.ect0 = 0;
+ fr->ack.ecn.ect1 = 64;
+ fr->ack.ecn.ce = 16384;
+
+ framelen = 1 + 8 + 4 + 1 + 8 + (2 + 8) + (1 + 8) + 1 + 2 + 4;
+
+ rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr->type == nfr->type);
+ CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack);
+ CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay);
+ CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt);
+
+ for (i = 0; i < fr->ack.rangecnt; ++i) {
+ CU_ASSERT(fr->ack.ranges[i].gap == nfr->ack.ranges[i].gap);
+ CU_ASSERT(fr->ack.ranges[i].len == nfr->ack.ranges[i].len);
+ }
+
+ CU_ASSERT(fr->ack.ecn.ect0 == nfr->ack.ecn.ect0);
+ CU_ASSERT(fr->ack.ecn.ect1 == nfr->ack.ecn.ect1);
+ CU_ASSERT(fr->ack.ecn.ce == nfr->ack.ecn.ce);
+
+ memset(&nmfr, 0, sizeof(nmfr));
+}
+
+void test_ngtcp2_pkt_encode_reset_stream_frame(void) {
+ uint8_t buf[32];
+ ngtcp2_reset_stream fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 4 + 4 + 8;
+
+ fr.type = NGTCP2_FRAME_RESET_STREAM;
+ fr.stream_id = 1000000007;
+ fr.app_error_code = 0xe1e2;
+ fr.final_size = 0x31f2f3f4f5f6f7f8llu;
+
+ rv = ngtcp2_pkt_encode_reset_stream_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_reset_stream_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.stream_id == nfr.stream_id);
+ CU_ASSERT(fr.app_error_code == nfr.app_error_code);
+ CU_ASSERT(fr.final_size == nfr.final_size);
+}
+
+void test_ngtcp2_pkt_encode_connection_close_frame(void) {
+ uint8_t buf[2048];
+ ngtcp2_frame fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+ uint8_t reason[1024];
+
+ memset(reason, 0xfa, sizeof(reason));
+
+ /* no Reason Phrase */
+ fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
+ fr.connection_close.error_code = 0xf1f2u;
+ fr.connection_close.frame_type = 255;
+ fr.connection_close.reasonlen = 0;
+ fr.connection_close.reason = NULL;
+
+ framelen = 1 + 4 + 2 + 1;
+
+ rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf),
+ &fr.connection_close);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_connection_close_frame(&nfr.connection_close, buf,
+ framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.connection_close.error_code == nfr.connection_close.error_code);
+ CU_ASSERT(fr.connection_close.reasonlen == nfr.connection_close.reasonlen);
+ CU_ASSERT(fr.connection_close.reason == nfr.connection_close.reason);
+
+ memset(&nfr, 0, sizeof(nfr));
+
+ /* 1024 bytes Reason Phrase */
+ fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
+ fr.connection_close.error_code = 0xf3f4u;
+ fr.connection_close.frame_type = 0;
+ fr.connection_close.reasonlen = sizeof(reason);
+ fr.connection_close.reason = reason;
+
+ framelen = 1 + 4 + 1 + 2 + sizeof(reason);
+
+ rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf),
+ &fr.connection_close);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_connection_close_frame(&nfr.connection_close, buf,
+ framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.connection_close.error_code == nfr.connection_close.error_code);
+ CU_ASSERT(fr.connection_close.reasonlen == nfr.connection_close.reasonlen);
+ CU_ASSERT(0 == memcmp(reason, nfr.connection_close.reason, sizeof(reason)));
+
+ memset(&nfr, 0, sizeof(nfr));
+}
+
+void test_ngtcp2_pkt_encode_connection_close_app_frame(void) {
+ uint8_t buf[2048];
+ ngtcp2_frame fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+ uint8_t reason[1024];
+
+ memset(reason, 0xfa, sizeof(reason));
+
+ /* no Reason Phrase */
+ fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP;
+ fr.connection_close.error_code = 0xf1f2u;
+ fr.connection_close.frame_type = 0xff; /* This must be ignored. */
+ fr.connection_close.reasonlen = 0;
+ fr.connection_close.reason = NULL;
+
+ framelen = 1 + 4 + 1;
+
+ rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf),
+ &fr.connection_close);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_connection_close_frame(&nfr.connection_close, buf,
+ framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.connection_close.error_code == nfr.connection_close.error_code);
+ CU_ASSERT(0 == nfr.connection_close.frame_type);
+ CU_ASSERT(fr.connection_close.reasonlen == nfr.connection_close.reasonlen);
+ CU_ASSERT(fr.connection_close.reason == nfr.connection_close.reason);
+
+ memset(&nfr, 0, sizeof(nfr));
+}
+
+void test_ngtcp2_pkt_encode_max_data_frame(void) {
+ uint8_t buf[16];
+ ngtcp2_max_data fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8;
+
+ fr.type = NGTCP2_FRAME_MAX_DATA;
+ fr.max_data = 0x31f2f3f4f5f6f7f8llu;
+
+ rv = ngtcp2_pkt_encode_max_data_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_max_data_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.max_data == nfr.max_data);
+}
+
+void test_ngtcp2_pkt_encode_max_stream_data_frame(void) {
+ uint8_t buf[17];
+ ngtcp2_max_stream_data fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8 + 8;
+
+ fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ fr.stream_id = 0xf1f2f3f4u;
+ fr.max_stream_data = 0x35f6f7f8f9fafbfcllu;
+
+ rv = ngtcp2_pkt_encode_max_stream_data_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_max_stream_data_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.stream_id == nfr.stream_id);
+ CU_ASSERT(fr.max_stream_data == nfr.max_stream_data);
+}
+
+void test_ngtcp2_pkt_encode_max_streams_frame(void) {
+ uint8_t buf[16];
+ ngtcp2_max_streams fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8;
+
+ fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI;
+ fr.max_streams = 0xf1f2f3f4u;
+
+ rv = ngtcp2_pkt_encode_max_streams_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_max_streams_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.max_streams == nfr.max_streams);
+}
+
+void test_ngtcp2_pkt_encode_ping_frame(void) {
+ uint8_t buf[3];
+ ngtcp2_ping fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+
+ fr.type = NGTCP2_FRAME_PING;
+
+ framelen = 1;
+
+ rv = ngtcp2_pkt_encode_ping_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_ping_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+}
+
+void test_ngtcp2_pkt_encode_data_blocked_frame(void) {
+ uint8_t buf[9];
+ ngtcp2_data_blocked fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8;
+
+ fr.type = NGTCP2_FRAME_DATA_BLOCKED;
+ fr.offset = 0x31f2f3f4f5f6f7f8llu;
+
+ rv = ngtcp2_pkt_encode_data_blocked_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_data_blocked_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.offset == nfr.offset);
+}
+
+void test_ngtcp2_pkt_encode_stream_data_blocked_frame(void) {
+ uint8_t buf[17];
+ ngtcp2_stream_data_blocked fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8 + 8;
+
+ fr.type = NGTCP2_FRAME_STREAM_DATA_BLOCKED;
+ fr.stream_id = 0xf1f2f3f4u;
+ fr.offset = 0x35f6f7f8f9fafbfcllu;
+
+ rv = ngtcp2_pkt_encode_stream_data_blocked_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_stream_data_blocked_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.stream_id == nfr.stream_id);
+ CU_ASSERT(fr.offset == nfr.offset);
+}
+
+void test_ngtcp2_pkt_encode_streams_blocked_frame(void) {
+ uint8_t buf[9];
+ ngtcp2_streams_blocked fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8;
+
+ fr.type = NGTCP2_FRAME_STREAMS_BLOCKED_BIDI;
+ fr.max_streams = 0xf1f2f3f4u;
+
+ rv = ngtcp2_pkt_encode_streams_blocked_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_streams_blocked_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.max_streams == nfr.max_streams);
+}
+
+void test_ngtcp2_pkt_encode_new_connection_id_frame(void) {
+ uint8_t buf[256];
+ ngtcp2_new_connection_id fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 4 + 2 + 1 + 18 + NGTCP2_STATELESS_RESET_TOKENLEN;
+
+ fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ fr.seq = 1000000009;
+ fr.retire_prior_to = 255;
+ scid_init(&fr.cid);
+ memset(fr.stateless_reset_token, 0xe1, sizeof(fr.stateless_reset_token));
+
+ rv = ngtcp2_pkt_encode_new_connection_id_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_new_connection_id_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.seq == nfr.seq);
+ CU_ASSERT(ngtcp2_cid_eq(&fr.cid, &nfr.cid));
+ CU_ASSERT(0 == memcmp(fr.stateless_reset_token, nfr.stateless_reset_token,
+ sizeof(fr.stateless_reset_token)));
+}
+
+void test_ngtcp2_pkt_encode_stop_sending_frame(void) {
+ uint8_t buf[16];
+ ngtcp2_stop_sending fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8 + 4;
+
+ fr.type = NGTCP2_FRAME_STOP_SENDING;
+ fr.stream_id = 0xf1f2f3f4u;
+ fr.app_error_code = 0xe1e2u;
+
+ rv = ngtcp2_pkt_encode_stop_sending_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_stop_sending_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.stream_id == nfr.stream_id);
+ CU_ASSERT(fr.app_error_code == nfr.app_error_code);
+}
+
+void test_ngtcp2_pkt_encode_path_challenge_frame(void) {
+ uint8_t buf[9];
+ ngtcp2_path_challenge fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8;
+ size_t i;
+
+ fr.type = NGTCP2_FRAME_PATH_CHALLENGE;
+ for (i = 0; i < sizeof(fr.data); ++i) {
+ fr.data[i] = (uint8_t)(i + 1);
+ }
+
+ rv = ngtcp2_pkt_encode_path_challenge_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_path_challenge_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(0 == memcmp(fr.data, nfr.data, sizeof(fr.data)));
+}
+
+void test_ngtcp2_pkt_encode_path_response_frame(void) {
+ uint8_t buf[9];
+ ngtcp2_path_response fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1 + 8;
+ size_t i;
+
+ fr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ for (i = 0; i < sizeof(fr.data); ++i) {
+ fr.data[i] = (uint8_t)(i + 1);
+ }
+
+ rv = ngtcp2_pkt_encode_path_response_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_path_response_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(0 == memcmp(fr.data, nfr.data, sizeof(fr.data)));
+}
+
+void test_ngtcp2_pkt_encode_crypto_frame(void) {
+ const uint8_t data[] = "0123456789abcdef1";
+ uint8_t buf[256];
+ ngtcp2_frame fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+
+ fr.type = NGTCP2_FRAME_CRYPTO;
+ fr.crypto.offset = 0x31f2f3f4f5f6f7f8llu;
+ fr.crypto.datacnt = 1;
+ fr.crypto.data[0].len = strsize(data);
+ fr.crypto.data[0].base = (uint8_t *)data;
+
+ framelen = 1 + 8 + 1 + 17;
+
+ rv = ngtcp2_pkt_encode_crypto_frame(buf, sizeof(buf), &fr.crypto);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_crypto_frame(&nfr.crypto, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.crypto.offset == nfr.crypto.offset);
+ CU_ASSERT(fr.crypto.datacnt == nfr.crypto.datacnt);
+ CU_ASSERT(fr.crypto.data[0].len == nfr.crypto.data[0].len);
+ CU_ASSERT(0 == memcmp(fr.crypto.data[0].base, nfr.crypto.data[0].base,
+ fr.crypto.data[0].len));
+}
+
+void test_ngtcp2_pkt_encode_new_token_frame(void) {
+ const uint8_t token[] = "0123456789abcdef2";
+ uint8_t buf[256];
+ ngtcp2_frame fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+
+ fr.type = NGTCP2_FRAME_NEW_TOKEN;
+ ngtcp2_vec_init(&fr.new_token.token, token, strsize(token));
+
+ framelen = 1 + 1 + strsize(token);
+
+ rv = ngtcp2_pkt_encode_new_token_frame(buf, sizeof(buf), &fr.new_token);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_new_token_frame(&nfr.new_token, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.new_token.token.len == nfr.new_token.token.len);
+ CU_ASSERT(0 == memcmp(fr.new_token.token.base, nfr.new_token.token.base,
+ fr.new_token.token.len));
+}
+
+void test_ngtcp2_pkt_encode_retire_connection_id_frame(void) {
+ uint8_t buf[256];
+ ngtcp2_frame fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+
+ fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+ fr.retire_connection_id.seq = 1000000007;
+
+ framelen = 1 + ngtcp2_put_uvarintlen(fr.retire_connection_id.seq);
+
+ rv = ngtcp2_pkt_encode_retire_connection_id_frame(buf, sizeof(buf),
+ &fr.retire_connection_id);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_retire_connection_id_frame(&nfr.retire_connection_id,
+ buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.retire_connection_id.seq == nfr.retire_connection_id.seq);
+}
+
+void test_ngtcp2_pkt_encode_handshake_done_frame(void) {
+ uint8_t buf[16];
+ ngtcp2_handshake_done fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen = 1;
+
+ fr.type = NGTCP2_FRAME_HANDSHAKE_DONE;
+
+ rv = ngtcp2_pkt_encode_handshake_done_frame(buf, sizeof(buf), &fr);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_handshake_done_frame(&nfr, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+}
+
+void test_ngtcp2_pkt_encode_datagram_frame(void) {
+ const uint8_t data[] = "0123456789abcdef3";
+ uint8_t buf[256];
+ ngtcp2_frame fr, nfr;
+ ngtcp2_ssize rv;
+ size_t framelen;
+
+ fr.type = NGTCP2_FRAME_DATAGRAM_LEN;
+ fr.datagram.datacnt = 1;
+ fr.datagram.data = fr.datagram.rdata;
+ fr.datagram.rdata[0].len = strsize(data);
+ fr.datagram.rdata[0].base = (uint8_t *)data;
+
+ framelen = 1 + 1 + 17;
+
+ rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt);
+ CU_ASSERT(fr.datagram.data->len == nfr.datagram.data->len);
+ CU_ASSERT(0 == memcmp(fr.datagram.data->base, nfr.datagram.data->base,
+ fr.datagram.data->len));
+
+ memset(&nfr, 0, sizeof(nfr));
+
+ /* Without length field */
+ fr.type = NGTCP2_FRAME_DATAGRAM;
+ fr.datagram.datacnt = 1;
+ fr.datagram.data = fr.datagram.rdata;
+ fr.datagram.rdata[0].len = strsize(data);
+ fr.datagram.rdata[0].base = (uint8_t *)data;
+
+ framelen = 1 + 17;
+
+ rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt);
+ CU_ASSERT(fr.datagram.data->len == nfr.datagram.data->len);
+ CU_ASSERT(0 == memcmp(fr.datagram.data->base, nfr.datagram.data->base,
+ fr.datagram.data->len));
+
+ memset(&nfr, 0, sizeof(nfr));
+
+ /* Zero length data with length field */
+ fr.type = NGTCP2_FRAME_DATAGRAM_LEN;
+ fr.datagram.datacnt = 0;
+ fr.datagram.data = NULL;
+
+ framelen = 1 + 1;
+
+ rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt);
+ CU_ASSERT(NULL == nfr.datagram.data);
+
+ /* Zero length data without length field */
+ fr.type = NGTCP2_FRAME_DATAGRAM;
+ fr.datagram.datacnt = 0;
+ fr.datagram.data = NULL;
+
+ framelen = 1;
+
+ rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+ rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen);
+
+ CU_ASSERT((ngtcp2_ssize)framelen == rv);
+ CU_ASSERT(fr.type == nfr.type);
+ CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt);
+ CU_ASSERT(NULL == nfr.datagram.data);
+}
+
+void test_ngtcp2_pkt_adjust_pkt_num(void) {
+ CU_ASSERT(0xaa831f94llu ==
+ ngtcp2_pkt_adjust_pkt_num(0xaa82f30ellu, 0x1f94, 16));
+
+ CU_ASSERT(0xff == ngtcp2_pkt_adjust_pkt_num(0x0100, 0xff, 8));
+ CU_ASSERT(0x01ff == ngtcp2_pkt_adjust_pkt_num(0x01ff, 0xff, 8));
+ CU_ASSERT(0x0fff == ngtcp2_pkt_adjust_pkt_num(0x1000, 0xff, 8));
+ CU_ASSERT(0x80 == ngtcp2_pkt_adjust_pkt_num(0x00, 0x80, 8));
+ CU_ASSERT(0x3fffffffffffffabllu ==
+ ngtcp2_pkt_adjust_pkt_num(NGTCP2_MAX_PKT_NUM, 0xab, 8));
+ CU_ASSERT(0x4000000000000000llu ==
+ ngtcp2_pkt_adjust_pkt_num(NGTCP2_MAX_PKT_NUM, 0x00, 8));
+ CU_ASSERT(250 == ngtcp2_pkt_adjust_pkt_num(255, 250, 8));
+ CU_ASSERT(8 == ngtcp2_pkt_adjust_pkt_num(50, 8, 8));
+ CU_ASSERT(0 == ngtcp2_pkt_adjust_pkt_num(-1, 0, 8));
+}
+
+void test_ngtcp2_pkt_validate_ack(void) {
+ int rv;
+ ngtcp2_ack fr;
+
+ /* too long first_ack_range */
+ fr.largest_ack = 1;
+ fr.first_ack_range = 2;
+ fr.rangecnt = 0;
+
+ rv = ngtcp2_pkt_validate_ack(&fr);
+
+ CU_ASSERT(NGTCP2_ERR_ACK_FRAME == rv);
+
+ /* gap is too large */
+ fr.largest_ack = 250;
+ fr.first_ack_range = 1;
+ fr.rangecnt = 1;
+ fr.ranges[0].gap = 248;
+ fr.ranges[0].len = 0;
+
+ rv = ngtcp2_pkt_validate_ack(&fr);
+
+ CU_ASSERT(NGTCP2_ERR_ACK_FRAME == rv);
+
+ /* too large range len */
+ fr.largest_ack = 250;
+ fr.first_ack_range = 0;
+ fr.rangecnt = 1;
+ fr.ranges[0].gap = 248;
+ fr.ranges[0].len = 1;
+
+ rv = ngtcp2_pkt_validate_ack(&fr);
+
+ CU_ASSERT(NGTCP2_ERR_ACK_FRAME == rv);
+}
+
+void test_ngtcp2_pkt_write_stateless_reset(void) {
+ uint8_t buf[256];
+ ngtcp2_ssize spktlen;
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ uint8_t rand[256];
+ size_t i;
+ uint8_t *p;
+ size_t randlen;
+
+ memset(rand, 0, sizeof(rand));
+ for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) {
+ token[i] = (uint8_t)(i + 1);
+ }
+
+ spktlen = ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), token, rand,
+ sizeof(rand));
+
+ p = buf;
+
+ CU_ASSERT(256 == spktlen);
+ CU_ASSERT(0 == (*p & NGTCP2_HEADER_FORM_BIT));
+ CU_ASSERT((*p & NGTCP2_FIXED_BIT_MASK));
+
+ ++p;
+
+ randlen = (size_t)(spktlen - (p - buf) - NGTCP2_STATELESS_RESET_TOKENLEN);
+
+ CU_ASSERT(0 == memcmp(rand, p, randlen));
+
+ p += randlen;
+
+ CU_ASSERT(0 == memcmp(token, p, NGTCP2_STATELESS_RESET_TOKENLEN));
+
+ p += NGTCP2_STATELESS_RESET_TOKENLEN;
+
+ CU_ASSERT(spktlen == p - buf);
+
+ /* Not enough buffer */
+ spktlen = ngtcp2_pkt_write_stateless_reset(
+ buf,
+ NGTCP2_MIN_STATELESS_RESET_RANDLEN - 1 + NGTCP2_STATELESS_RESET_TOKENLEN,
+ token, rand, sizeof(rand));
+
+ CU_ASSERT(NGTCP2_ERR_NOBUF == spktlen);
+}
+
+void test_ngtcp2_pkt_write_retry(void) {
+ uint8_t buf[256];
+ ngtcp2_ssize spktlen;
+ ngtcp2_cid scid, dcid, odcid;
+ ngtcp2_pkt_hd nhd;
+ uint8_t token[32];
+ size_t i;
+ ngtcp2_pkt_retry retry;
+ ngtcp2_ssize nread;
+ int rv;
+ ngtcp2_crypto_aead aead = {0};
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ uint8_t tag[NGTCP2_RETRY_TAGLEN] = {0};
+
+ scid_init(&scid);
+ dcid_init(&dcid);
+ rcid_init(&odcid);
+
+ for (i = 0; i < sizeof(token); ++i) {
+ token[i] = (uint8_t)i;
+ }
+
+ spktlen = ngtcp2_pkt_write_retry(buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &dcid,
+ &scid, &odcid, token, sizeof(token),
+ null_retry_encrypt, &aead, &aead_ctx);
+
+ CU_ASSERT(spktlen > 0);
+
+ memset(&nhd, 0, sizeof(nhd));
+
+ nread = ngtcp2_pkt_decode_hd_long(&nhd, buf, (size_t)spktlen);
+
+ CU_ASSERT(nread > 0);
+ CU_ASSERT(NGTCP2_PKT_RETRY == nhd.type);
+ CU_ASSERT(NGTCP2_PROTO_VER_V1 == nhd.version);
+ CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid));
+ CU_ASSERT(ngtcp2_cid_eq(&scid, &nhd.scid));
+
+ rv = ngtcp2_pkt_decode_retry(&retry, buf + nread, (size_t)(spktlen - nread));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(sizeof(token) == retry.token.len);
+ CU_ASSERT(0 == memcmp(token, retry.token.base, sizeof(token)));
+ CU_ASSERT(0 == memcmp(tag, retry.tag, sizeof(tag)));
+}
+
+void test_ngtcp2_pkt_write_version_negotiation(void) {
+ uint8_t buf[256];
+ ngtcp2_ssize spktlen;
+ const uint32_t sv[] = {0xf1f2f3f4, 0x1f2f3f4f};
+ const uint8_t *p;
+ size_t i;
+ ngtcp2_cid dcid, scid;
+ uint32_t v;
+
+ dcid_init(&dcid);
+ scid_init(&scid);
+
+ spktlen = ngtcp2_pkt_write_version_negotiation(
+ buf, sizeof(buf), 133, dcid.data, dcid.datalen, scid.data, scid.datalen,
+ sv, ngtcp2_arraylen(sv));
+
+ CU_ASSERT((ngtcp2_ssize)(1 + 4 + 1 + dcid.datalen + 1 + scid.datalen +
+ ngtcp2_arraylen(sv) * 4) == spktlen);
+
+ p = buf;
+
+ CU_ASSERT((0x80 | 133) == buf[0]);
+
+ ++p;
+
+ p = ngtcp2_get_uint32(&v, p);
+
+ CU_ASSERT(0 == v);
+
+ CU_ASSERT(dcid.datalen == *p);
+
+ ++p;
+
+ CU_ASSERT(0 == memcmp(dcid.data, p, dcid.datalen));
+
+ p += dcid.datalen;
+
+ CU_ASSERT(scid.datalen == *p);
+
+ ++p;
+
+ CU_ASSERT(0 == memcmp(scid.data, p, scid.datalen));
+
+ p += scid.datalen;
+
+ for (i = 0; i < ngtcp2_arraylen(sv); ++i) {
+ p = ngtcp2_get_uint32(&v, p);
+
+ CU_ASSERT(sv[i] == v);
+ }
+}
+
+void test_ngtcp2_pkt_stream_max_datalen(void) {
+ size_t len;
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 0, 2);
+
+ CU_ASSERT((size_t)-1 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 0, 3);
+
+ CU_ASSERT(0 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 1, 3);
+
+ CU_ASSERT(0 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 1, 4);
+
+ CU_ASSERT(1 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 1, 1, 4);
+
+ CU_ASSERT(0 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 63, 66);
+
+ CU_ASSERT(63 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 63, 65);
+
+ CU_ASSERT(62 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 1400);
+
+ CU_ASSERT(1396 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 1399);
+
+ CU_ASSERT(1395 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 9);
+
+ CU_ASSERT(6 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 16385, 16391);
+
+ CU_ASSERT(16385 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 16385, 16390);
+
+ CU_ASSERT(16384 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 1073741824, 1073741834);
+
+ CU_ASSERT(1073741824 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 1073741824, 1073741833);
+
+ CU_ASSERT(1073741823 == len);
+
+ len = ngtcp2_pkt_stream_max_datalen(63, 0, 16383, 16387);
+
+ CU_ASSERT(16383 == len);
+}
diff --git a/tests/ngtcp2_pkt_test.h b/tests/ngtcp2_pkt_test.h
new file mode 100644
index 0000000..52505eb
--- /dev/null
+++ b/tests/ngtcp2_pkt_test.h
@@ -0,0 +1,68 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PKT_TEST_H
+#define NGTCP2_PKT_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_pkt_decode_version_cid(void);
+void test_ngtcp2_pkt_decode_hd_long(void);
+void test_ngtcp2_pkt_decode_hd_short(void);
+void test_ngtcp2_pkt_decode_stream_frame(void);
+void test_ngtcp2_pkt_decode_ack_frame(void);
+void test_ngtcp2_pkt_decode_padding_frame(void);
+void test_ngtcp2_pkt_encode_stream_frame(void);
+void test_ngtcp2_pkt_encode_ack_frame(void);
+void test_ngtcp2_pkt_encode_ack_ecn_frame(void);
+void test_ngtcp2_pkt_encode_reset_stream_frame(void);
+void test_ngtcp2_pkt_encode_connection_close_frame(void);
+void test_ngtcp2_pkt_encode_connection_close_app_frame(void);
+void test_ngtcp2_pkt_encode_application_close_frame(void);
+void test_ngtcp2_pkt_encode_max_data_frame(void);
+void test_ngtcp2_pkt_encode_max_stream_data_frame(void);
+void test_ngtcp2_pkt_encode_max_streams_frame(void);
+void test_ngtcp2_pkt_encode_ping_frame(void);
+void test_ngtcp2_pkt_encode_data_blocked_frame(void);
+void test_ngtcp2_pkt_encode_stream_data_blocked_frame(void);
+void test_ngtcp2_pkt_encode_streams_blocked_frame(void);
+void test_ngtcp2_pkt_encode_new_connection_id_frame(void);
+void test_ngtcp2_pkt_encode_stop_sending_frame(void);
+void test_ngtcp2_pkt_encode_path_challenge_frame(void);
+void test_ngtcp2_pkt_encode_path_response_frame(void);
+void test_ngtcp2_pkt_encode_crypto_frame(void);
+void test_ngtcp2_pkt_encode_new_token_frame(void);
+void test_ngtcp2_pkt_encode_retire_connection_id_frame(void);
+void test_ngtcp2_pkt_encode_handshake_done_frame(void);
+void test_ngtcp2_pkt_encode_datagram_frame(void);
+void test_ngtcp2_pkt_adjust_pkt_num(void);
+void test_ngtcp2_pkt_validate_ack(void);
+void test_ngtcp2_pkt_write_stateless_reset(void);
+void test_ngtcp2_pkt_write_retry(void);
+void test_ngtcp2_pkt_write_version_negotiation(void);
+void test_ngtcp2_pkt_stream_max_datalen(void);
+
+#endif /* NGTCP2_PKT_TEST_H */
diff --git a/tests/ngtcp2_pmtud_test.c b/tests/ngtcp2_pmtud_test.c
new file mode 100644
index 0000000..16f4981
--- /dev/null
+++ b/tests/ngtcp2_pmtud_test.c
@@ -0,0 +1,153 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pmtud_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_pmtud.h"
+#include "ngtcp2_test_helper.h"
+
+void test_ngtcp2_pmtud_probe(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_pmtud *pmtud;
+ int rv;
+
+ /* Send probe and get success */
+ rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE, 1452, 0, mem);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == pmtud->mtu_idx);
+ CU_ASSERT(!ngtcp2_pmtud_finished(pmtud));
+ CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud));
+ CU_ASSERT(1454 - 48 == ngtcp2_pmtud_probelen(pmtud));
+
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 0);
+
+ CU_ASSERT(1 == pmtud->num_pkts_sent);
+ CU_ASSERT(2 == pmtud->expiry);
+ CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud));
+
+ ngtcp2_pmtud_handle_expiry(pmtud, 1);
+
+ CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud));
+
+ ngtcp2_pmtud_handle_expiry(pmtud, 2);
+
+ CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud));
+
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 2);
+
+ CU_ASSERT(2 == pmtud->num_pkts_sent);
+ CU_ASSERT(4 == pmtud->expiry);
+ CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud));
+
+ ngtcp2_pmtud_handle_expiry(pmtud, 4);
+
+ CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud));
+
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 4);
+
+ CU_ASSERT(3 == pmtud->num_pkts_sent);
+ CU_ASSERT(10 == pmtud->expiry);
+ CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud));
+
+ ngtcp2_pmtud_probe_success(pmtud, ngtcp2_pmtud_probelen(pmtud));
+
+ CU_ASSERT(3 == pmtud->mtu_idx);
+ CU_ASSERT(UINT64_MAX == pmtud->expiry);
+ CU_ASSERT(0 == pmtud->num_pkts_sent);
+ CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud));
+ CU_ASSERT(1492 - 48 == ngtcp2_pmtud_probelen(pmtud));
+
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 10);
+ ngtcp2_pmtud_handle_expiry(pmtud, 12);
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 12);
+ ngtcp2_pmtud_handle_expiry(pmtud, 14);
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 14);
+ ngtcp2_pmtud_handle_expiry(pmtud, 20);
+
+ CU_ASSERT(1492 - 48 == pmtud->min_fail_udp_payload_size);
+ CU_ASSERT(ngtcp2_pmtud_finished(pmtud));
+
+ ngtcp2_pmtud_del(pmtud);
+
+ /* Failing 2nd probe should skip the third probe */
+ rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE, 1452, 0, mem);
+
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 0);
+ ngtcp2_pmtud_handle_expiry(pmtud, 2);
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 2);
+ ngtcp2_pmtud_handle_expiry(pmtud, 4);
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 4);
+ ngtcp2_pmtud_handle_expiry(pmtud, 10);
+
+ CU_ASSERT(1454 - 48 == pmtud->min_fail_udp_payload_size);
+ CU_ASSERT(1 == pmtud->mtu_idx);
+
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 10);
+ ngtcp2_pmtud_handle_expiry(pmtud, 12);
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 12);
+ ngtcp2_pmtud_handle_expiry(pmtud, 14);
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 14);
+ ngtcp2_pmtud_handle_expiry(pmtud, 20);
+
+ CU_ASSERT(1390 - 48 == pmtud->min_fail_udp_payload_size);
+ CU_ASSERT(2 == pmtud->mtu_idx);
+
+ ngtcp2_pmtud_probe_sent(pmtud, 2, 10);
+ ngtcp2_pmtud_probe_success(pmtud, 1280 - 48);
+
+ CU_ASSERT(ngtcp2_pmtud_finished(pmtud));
+
+ ngtcp2_pmtud_del(pmtud);
+
+ /* Skip 1st probe because it is larger than hard max. */
+ rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE, 1454 - 48 - 1, 0,
+ mem);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == pmtud->mtu_idx);
+
+ ngtcp2_pmtud_del(pmtud);
+
+ /* PMTUD finishes immediately because we know that all candidates
+ are lower than the current maximum. */
+ rv = ngtcp2_pmtud_new(&pmtud, 1492 - 48, 1452, 0, mem);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_pmtud_finished(pmtud));
+
+ ngtcp2_pmtud_del(pmtud);
+
+ /* PMTUD finishes immediately because the hard maximum size is lower
+ than the candidates. */
+ rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE,
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE, 0, mem);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(ngtcp2_pmtud_finished(pmtud));
+
+ ngtcp2_pmtud_del(pmtud);
+}
diff --git a/tests/ngtcp2_pmtud_test.h b/tests/ngtcp2_pmtud_test.h
new file mode 100644
index 0000000..c4e802e
--- /dev/null
+++ b/tests/ngtcp2_pmtud_test.h
@@ -0,0 +1,34 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PMTUD_TEST_H
+#define NGTCP2_PMTUD_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_pmtud_probe(void);
+
+#endif /* NGTCP2_PMTUD_TEST_H */
diff --git a/tests/ngtcp2_pv_test.c b/tests/ngtcp2_pv_test.c
new file mode 100644
index 0000000..6683df1
--- /dev/null
+++ b/tests/ngtcp2_pv_test.c
@@ -0,0 +1,129 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pv_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_pv.h"
+#include "ngtcp2_test_helper.h"
+
+void test_ngtcp2_pv_add_entry(void) {
+ ngtcp2_pv *pv;
+ int rv;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_cid cid;
+ const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff};
+ ngtcp2_dcid dcid;
+ ngtcp2_log log;
+ uint8_t data[8];
+ size_t i;
+ ngtcp2_duration timeout = 100ULL * NGTCP2_SECONDS;
+
+ dcid_init(&cid);
+ ngtcp2_dcid_init(&dcid, 1000000007, &cid, token);
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+
+ rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_NONE, &log, mem);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == ngtcp2_pv_validation_timed_out(pv, 0));
+
+ ngtcp2_pv_handle_entry_expiry(pv, 0);
+
+ CU_ASSERT(NGTCP2_PV_NUM_PROBE_PKT == pv->probe_pkt_left);
+ CU_ASSERT(ngtcp2_pv_should_send_probe(pv));
+
+ for (i = 0; i < NGTCP2_PV_NUM_PROBE_PKT; ++i) {
+ ngtcp2_pv_add_entry(pv, data, 100, NGTCP2_PV_ENTRY_FLAG_NONE, 0);
+
+ CU_ASSERT(i + 1 == ngtcp2_ringbuf_len(&pv->ents.rb));
+ }
+
+ CU_ASSERT(0 == pv->probe_pkt_left);
+ CU_ASSERT(!ngtcp2_pv_should_send_probe(pv));
+ CU_ASSERT(NGTCP2_PV_NUM_PROBE_PKT == ngtcp2_ringbuf_len(&pv->ents.rb));
+ CU_ASSERT(100 == ngtcp2_pv_next_expiry(pv));
+
+ ngtcp2_pv_handle_entry_expiry(pv, 99);
+
+ CU_ASSERT(0 == pv->probe_pkt_left);
+ CU_ASSERT(!ngtcp2_pv_should_send_probe(pv));
+
+ ngtcp2_pv_handle_entry_expiry(pv, 100);
+
+ CU_ASSERT(2 == pv->probe_pkt_left);
+ CU_ASSERT(ngtcp2_pv_should_send_probe(pv));
+ CU_ASSERT(100 == ngtcp2_pv_next_expiry(pv));
+
+ ngtcp2_pv_add_entry(pv, data, 111, NGTCP2_PV_ENTRY_FLAG_NONE, 100);
+
+ CU_ASSERT(1 == pv->probe_pkt_left);
+ CU_ASSERT(ngtcp2_pv_should_send_probe(pv));
+ CU_ASSERT(111 == ngtcp2_pv_next_expiry(pv));
+
+ ngtcp2_pv_del(pv);
+}
+
+void test_ngtcp2_pv_validate(void) {
+ ngtcp2_pv *pv;
+ int rv;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_cid cid;
+ const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff};
+ ngtcp2_dcid dcid;
+ ngtcp2_log log;
+ uint8_t data[8];
+ ngtcp2_duration timeout = 100ULL * NGTCP2_SECONDS;
+ ngtcp2_path_storage path;
+ uint8_t flags;
+
+ path_init(&path, 1, 0, 2, 0);
+ dcid_init(&cid);
+ ngtcp2_dcid_init(&dcid, 1000000007, &cid, token);
+ ngtcp2_path_copy(&dcid.ps.path, &path.path);
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+
+ rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_NONE, &log, mem);
+
+ CU_ASSERT(0 == rv);
+
+ memset(data, 0, sizeof(data));
+ ngtcp2_pv_add_entry(pv, data, 100, NGTCP2_PV_ENTRY_FLAG_NONE, 1);
+
+ memset(data, 1, sizeof(data));
+ ngtcp2_pv_add_entry(pv, data, 100, NGTCP2_PV_ENTRY_FLAG_NONE, 1);
+
+ memset(data, 1, sizeof(data));
+ rv = ngtcp2_pv_validate(pv, &flags, data);
+
+ CU_ASSERT(0 == rv);
+
+ memset(data, 3, sizeof(data));
+ rv = ngtcp2_pv_validate(pv, &flags, data);
+
+ CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv);
+
+ ngtcp2_pv_del(pv);
+}
diff --git a/tests/ngtcp2_pv_test.h b/tests/ngtcp2_pv_test.h
new file mode 100644
index 0000000..331dc09
--- /dev/null
+++ b/tests/ngtcp2_pv_test.h
@@ -0,0 +1,35 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PV_TEST_H
+#define NGTCP2_PV_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_pv_add_entry(void);
+void test_ngtcp2_pv_validate(void);
+
+#endif /* NGTCP2_PV_TEST_H */
diff --git a/tests/ngtcp2_range_test.c b/tests/ngtcp2_range_test.c
new file mode 100644
index 0000000..d60c343
--- /dev/null
+++ b/tests/ngtcp2_range_test.c
@@ -0,0 +1,105 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_range_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_range.h"
+#include "ngtcp2_test_helper.h"
+
+void test_ngtcp2_range_intersect(void) {
+ ngtcp2_range a, b, c;
+
+ ngtcp2_range_init(&a, 0, UINT64_MAX);
+ ngtcp2_range_init(&b, 0, 1000000007);
+ c = ngtcp2_range_intersect(&a, &b);
+
+ CU_ASSERT(0 == c.begin);
+ CU_ASSERT(1000000007 == c.end);
+
+ ngtcp2_range_init(&a, 1000000007, UINT64_MAX);
+ ngtcp2_range_init(&b, 0, UINT64_MAX);
+ c = ngtcp2_range_intersect(&a, &b);
+
+ CU_ASSERT(1000000007 == c.begin);
+ CU_ASSERT(UINT64_MAX == c.end);
+
+ ngtcp2_range_init(&a, 0, UINT64_MAX);
+ ngtcp2_range_init(&b, 33333, 55555);
+ c = ngtcp2_range_intersect(&a, &b);
+
+ CU_ASSERT(33333 == c.begin);
+ CU_ASSERT(55555 == c.end);
+
+ ngtcp2_range_init(&a, 0, 1000000009);
+ ngtcp2_range_init(&b, 1000000007, UINT64_MAX);
+ c = ngtcp2_range_intersect(&a, &b);
+
+ CU_ASSERT(1000000007 == c.begin);
+ CU_ASSERT(1000000009 == c.end);
+}
+
+void test_ngtcp2_range_cut(void) {
+ ngtcp2_range a, b, l, r;
+
+ ngtcp2_range_init(&a, 0, UINT64_MAX);
+ ngtcp2_range_init(&b, 1000000007, 1000000009);
+ ngtcp2_range_cut(&l, &r, &a, &b);
+
+ CU_ASSERT(0 == l.begin);
+ CU_ASSERT(1000000007 == l.end);
+ CU_ASSERT(1000000009 == r.begin);
+ CU_ASSERT(UINT64_MAX == r.end);
+
+ ngtcp2_range_init(&a, 0, UINT64_MAX);
+ ngtcp2_range_init(&b, 0, 1000000007);
+ ngtcp2_range_cut(&l, &r, &a, &b);
+
+ CU_ASSERT(0 == ngtcp2_range_len(&l));
+ CU_ASSERT(1000000007 == r.begin);
+ CU_ASSERT(UINT64_MAX == r.end);
+
+ ngtcp2_range_init(&a, 0, UINT64_MAX);
+ ngtcp2_range_init(&b, 1000000009, UINT64_MAX);
+ ngtcp2_range_cut(&l, &r, &a, &b);
+
+ CU_ASSERT(0 == l.begin);
+ CU_ASSERT(1000000009 == l.end);
+ CU_ASSERT(0 == ngtcp2_range_len(&r));
+}
+
+void test_ngtcp2_range_not_after(void) {
+ ngtcp2_range a, b;
+
+ ngtcp2_range_init(&a, 1, 1000000007);
+ ngtcp2_range_init(&b, 0, 1000000007);
+
+ CU_ASSERT(ngtcp2_range_not_after(&a, &b));
+
+ ngtcp2_range_init(&a, 1, 1000000008);
+ ngtcp2_range_init(&b, 0, 1000000007);
+
+ CU_ASSERT(!ngtcp2_range_not_after(&a, &b));
+}
diff --git a/tests/ngtcp2_range_test.h b/tests/ngtcp2_range_test.h
new file mode 100644
index 0000000..a8d2d9c
--- /dev/null
+++ b/tests/ngtcp2_range_test.h
@@ -0,0 +1,36 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RANGE_TEST_H
+#define NGTCP2_RANGE_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_range_intersect(void);
+void test_ngtcp2_range_cut(void);
+void test_ngtcp2_range_not_after(void);
+
+#endif /* NGTCP2_RANGE_TEST_H */
diff --git a/tests/ngtcp2_ringbuf_test.c b/tests/ngtcp2_ringbuf_test.c
new file mode 100644
index 0000000..b6440f5
--- /dev/null
+++ b/tests/ngtcp2_ringbuf_test.c
@@ -0,0 +1,91 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ringbuf_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_ringbuf.h"
+#include "ngtcp2_test_helper.h"
+
+typedef struct {
+ int32_t a;
+ uint64_t b;
+} ints;
+
+void test_ngtcp2_ringbuf_push_front(void) {
+ ngtcp2_ringbuf rb;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+
+ ngtcp2_ringbuf_init(&rb, 64, sizeof(ints), mem);
+
+ for (i = 0; i < 64; ++i) {
+ ints *p = ngtcp2_ringbuf_push_front(&rb);
+ p->a = (int32_t)(i + 1);
+ p->b = (i + 1) * 10;
+ }
+
+ CU_ASSERT(64 == ngtcp2_ringbuf_len(&rb));
+
+ for (i = 0; i < 64; ++i) {
+ ints *p = ngtcp2_ringbuf_get(&rb, i);
+ CU_ASSERT((int32_t)(64 - i) == p->a);
+ CU_ASSERT((64 - i) * 10 == p->b);
+ }
+
+ ngtcp2_ringbuf_push_front(&rb);
+
+ CU_ASSERT(64 == ngtcp2_ringbuf_len(&rb));
+ CU_ASSERT((int32_t)64 == ((ints *)ngtcp2_ringbuf_get(&rb, 1))->a);
+
+ ngtcp2_ringbuf_free(&rb);
+}
+
+void test_ngtcp2_ringbuf_pop_front(void) {
+ ngtcp2_ringbuf rb;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ size_t i;
+
+ ngtcp2_ringbuf_init(&rb, 4, sizeof(ints), mem);
+
+ for (i = 0; i < 5; ++i) {
+ ints *p = ngtcp2_ringbuf_push_front(&rb);
+ p->a = (int32_t)i;
+ }
+
+ CU_ASSERT(4 == ngtcp2_ringbuf_len(&rb));
+
+ for (i = 4; i >= 1; --i) {
+ ints *p = ngtcp2_ringbuf_get(&rb, 0);
+
+ CU_ASSERT((int32_t)i == p->a);
+
+ ngtcp2_ringbuf_pop_front(&rb);
+ }
+
+ CU_ASSERT(0 == ngtcp2_ringbuf_len(&rb));
+
+ ngtcp2_ringbuf_free(&rb);
+}
diff --git a/tests/ngtcp2_ringbuf_test.h b/tests/ngtcp2_ringbuf_test.h
new file mode 100644
index 0000000..7ad8b44
--- /dev/null
+++ b/tests/ngtcp2_ringbuf_test.h
@@ -0,0 +1,35 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RINGBUF_TEST_H
+#define NGTCP2_RINGBUF_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_ringbuf_push_front(void);
+void test_ngtcp2_ringbuf_pop_front(void);
+
+#endif /* NGTCP2_RINGBUF_TEST_H */
diff --git a/tests/ngtcp2_rob_test.c b/tests/ngtcp2_rob_test.c
new file mode 100644
index 0000000..93a2641
--- /dev/null
+++ b/tests/ngtcp2_rob_test.c
@@ -0,0 +1,552 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rob_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_rob.h"
+#include "ngtcp2_test_helper.h"
+#include "ngtcp2_mem.h"
+
+void test_ngtcp2_rob_push(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_rob rob;
+ int rv;
+ uint8_t data[256];
+ ngtcp2_rob_gap *g;
+ ngtcp2_ksl_it it;
+
+ /* Check range overlapping */
+ ngtcp2_rob_init(&rob, 64, mem);
+
+ rv = ngtcp2_rob_push(&rob, 34567, data, 145);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(0 == g->range.begin);
+ CU_ASSERT(34567 == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(34567 + 145 == g->range.begin);
+ CU_ASSERT(UINT64_MAX == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ rv = ngtcp2_rob_push(&rob, 34565, data, 1);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(0 == g->range.begin);
+ CU_ASSERT(34565 == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(34566 == g->range.begin);
+ CU_ASSERT(34567 == g->range.end);
+
+ rv = ngtcp2_rob_push(&rob, 34563, data, 1);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(0 == g->range.begin);
+ CU_ASSERT(34563 == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(34564 == g->range.begin);
+ CU_ASSERT(34565 == g->range.end);
+
+ rv = ngtcp2_rob_push(&rob, 34561, data, 151);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(0 == g->range.begin);
+ CU_ASSERT(34561 == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(34567 + 145 == g->range.begin);
+ CU_ASSERT(UINT64_MAX == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_rob_free(&rob);
+
+ /* Check removing prefix */
+ ngtcp2_rob_init(&rob, 64, mem);
+
+ rv = ngtcp2_rob_push(&rob, 0, data, 123);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(123 == g->range.begin);
+ CU_ASSERT(UINT64_MAX == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_rob_free(&rob);
+
+ /* Check removing suffix */
+ ngtcp2_rob_init(&rob, 64, mem);
+
+ rv = ngtcp2_rob_push(&rob, UINT64_MAX - 123, data, 123);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(0 == g->range.begin);
+ CU_ASSERT(UINT64_MAX - 123 == g->range.end);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_rob_free(&rob);
+}
+
+static ngtcp2_range randkeys[] = {
+ {25996, 26260}, {9431, 9555}, {9113, 9417}, {2992, 3408},
+ {35761, 36019}, {38891, 39113}, {30074, 30325}, {9525, 9953},
+ {31708, 31944}, {24554, 24864}, {13097, 13472}, {47253, 47400},
+ {18424, 18742}, {4618, 4889}, {40871, 41076}, {17642, 18068},
+ {47496, 47588}, {1226, 1283}, {17904, 18248}, {9221, 9488},
+ {8621, 8773}, {27912, 28344}, {5878, 6121}, {37336, 37545},
+ {15403, 15557}, {29314, 29450}, {2342, 2595}, {34000, 34356},
+ {46428, 46828}, {40624, 40703}, {47014, 47319}, {13353, 13635},
+ {14466, 14682}, {22446, 22654}, {10035, 10140}, {1005, 1410},
+ {3741, 4133}, {45734, 46053}, {7954, 8214}, {32666, 32796},
+ {45236, 45531}, {32100, 32501}, {25466, 25850}, {2845, 3179},
+ {23525, 23991}, {46367, 46459}, {37712, 38164}, {8506, 8680},
+ {31702, 31752}, {33364, 33825}, {14284, 14614}, {22928, 23344},
+ {29058, 29155}, {36639, 37014}, {29133, 29445}, {31071, 31478},
+ {40074, 40370}, {1263, 1383}, {7908, 8181}, {40426, 40716},
+ {4830, 5053}, {38241, 38645}, {51197, 51401}, {36180, 36301},
+ {14920, 15262}, {5707, 5882}, {32697, 32948}, {42324, 42791},
+ {1543, 1732}, {11037, 11395}, {36534, 36707}, {26093, 26322},
+ {41862, 42213}, {1373, 1745}, {31322, 31706}, {45474, 45851},
+ {19333, 19701}, {49172, 49524}, {10641, 10932}, {17459, 17630},
+ {5560, 5936}, {7657, 7988}, {3300, 3357}, {2496, 2600},
+ {46018, 46173}, {43127, 43239}, {48949, 49036}, {45094, 45412},
+ {8405, 8738}, {8687, 9168}, {41405, 41759}, {22014, 22474},
+ {16097, 16426}, {29611, 29931}, {46054, 46250}, {26305, 26545},
+ {13696, 13964}, {26899, 26981}, {30797, 30936}, {34125, 34235},
+ {50016, 50058}, {46775, 47005}, {4891, 5106}, {12720, 12994},
+ {44623, 44967}, {33597, 34060}, {50796, 51295}, {18862, 19242},
+ {36166, 36249}, {22237, 22583}, {18188, 18586}, {21376, 21447},
+ {49563, 49800}, {10121, 10272}, {39156, 39275}, {17609, 17866},
+ {47609, 47829}, {34311, 34631}, {2144, 2433}, {34692, 34824},
+ {8309, 8476}, {26969, 27447}, {40651, 40952}, {11906, 12116},
+ {22467, 22864}, {35535, 35941}, {33061, 33259}, {21006, 21364},
+ {15212, 15504}, {6954, 7356}, {6126, 6405}, {29268, 29514},
+ {35221, 35505}, {4163, 4350}, {17374, 17519}, {16170, 16511},
+ {37142, 37440}, {6288, 6556}, {27795, 28092}, {35381, 35476},
+ {1186, 1455}, {39834, 40197}, {3471, 3906}, {46871, 47242},
+ {40258, 40406}, {0, 306}, {31852, 32133}, {23314, 23408},
+ {37494, 37625}, {48742, 48990}, {37616, 37905}, {18615, 18991},
+ {2561, 2921}, {47767, 48139}, {39616, 39792}, {44791, 45046},
+ {2770, 3067}, {16697, 17083}, {9216, 9427}, {37661, 37774},
+ {14666, 14976}, {31547, 31819}, {36052, 36356}, {34989, 35285},
+ {1651, 2028}, {36264, 36515}, {10257, 10551}, {24381, 24628},
+ {28428, 28726}, {4242, 4576}, {44972, 45107}, {12970, 13213},
+ {19539, 19828}, {42541, 42763}, {20349, 20630}, {20138, 20418},
+ {10884, 11138}, {2717, 2908}, {8292, 8399}, {712, 1101},
+ {44451, 44741}, {28660, 28946}, {40955, 41253}, {29424, 29864},
+ {14177, 14446}, {30219, 30632}, {24757, 25012}, {47991, 48306},
+ {42054, 42252}, {3984, 4419}, {42304, 42506}, {7160, 7543},
+ {2004, 2152}, {9777, 10105}, {15724, 16008}, {11263, 11573},
+ {15066, 15239}, {12108, 12336}, {17138, 17570}, {30472, 30714},
+ {41197, 41294}, {24294, 24496}, {17371, 17514}, {11426, 11749},
+ {25223, 25474}, {18083, 18345}, {27611, 27919}, {8116, 8261},
+ {40317, 40373}, {46652, 47026}, {18082, 18151}, {19808, 19970},
+ {46627, 46885}, {11646, 11789}, {1498, 1687}, {35907, 36081},
+ {36340, 36593}, {1255, 1311}, {43485, 43551}, {6586, 6895},
+ {10331, 10467}, {26803, 26998}, {14007, 14360}, {35951, 36120},
+ {37327, 37592}, {35419, 35724}, {50379, 50514}, {37251, 37489},
+ {27313, 27752}, {27502, 27845}, {36608, 36732}, {41751, 42057},
+ {19118, 19267}, {16529, 16926}, {49794, 50066}, {37378, 37699},
+ {7440, 7552}, {10418, 10650}, {50184, 50635}, {44350, 44579},
+ {8178, 8502}, {33838, 34017}, {11582, 11864}, {11756, 11785},
+ {42136, 42328}, {39404, 39545}, {13924, 14209}, {29411, 29627},
+ {10836, 11139}, {40332, 40598}, {26097, 26561}, {5422, 5512},
+ {30687, 30849}, {4399, 4726}, {50679, 50762}, {41224, 41439},
+ {46023, 46129}, {22690, 23010}, {37920, 38085}, {25885, 26249},
+ {51047, 51185}, {21508, 21904}, {6731, 7010}, {38144, 38493},
+ {47648, 47886}, {120, 603}, {49964, 50182}, {43503, 43765},
+ {24092, 24436}, {19204, 19509}, {19668, 19930}, {6815, 6963},
+ {10552, 10775}, {949, 1239}, {36976, 37348}, {34806, 34901},
+ {19939, 20308}, {42245, 42329}, {42700, 43067}, {13821, 14054},
+ {28109, 28331}, {32929, 33212}, {23736, 24036}, {31969, 32240},
+ {12326, 12612}, {5999, 6132}, {42871, 43283}, {33204, 33496},
+ {5757, 5991}, {46826, 46927}, {4994, 5278}, {47371, 47713},
+ {20886, 21106}, {38457, 38794}, {48451, 48789}, {34146, 34343},
+ {45911, 46248}, {48215, 48615}, {43970, 44131}, {30886, 31216},
+ {50135, 50292}, {3726, 3854}, {39041, 39408}, {48617, 48756},
+ {46205, 46590}, {39766, 39923}, {20835, 21106}, {43716, 44066},
+ {45665, 45789}, {12549, 12755}, {23366, 23752}, {17864, 17942},
+ {28288, 28528}, {2744, 2941}, {49355, 49605}, {34527, 34816},
+ {23092, 23447}, {5832, 5912}, {21146, 21478}, {30784, 30884},
+ {28221, 28469}, {34944, 35047}, {23956, 24126}, {7538, 7890},
+ {32496, 32803}, {16404, 16607}, {37968, 38277}, {7399, 7574},
+ {28605, 28842}, {50454, 50851}, {20581, 20845}, {21395, 21705},
+ {50726, 50871}, {11953, 12278}, {533, 822}, {5298, 5658},
+ {48707, 48914}, {21760, 22223}, {1889, 2146}, {6409, 6842},
+ {44094, 44473}, {18003, 18336}, {41550, 41926}, {50042, 50136},
+ {38646, 38835}, {5425, 5693}, {48967, 49383}, {376, 596},
+ {47514, 47704}, {43238, 43663}, {25440, 25655}, {25652, 26050},
+ {16909, 17232}, {41312, 41490}, {5909, 6049}, {3153, 3523},
+ {27877, 28046}, {26715, 26810}, {10031, 10108}, {32282, 32620},
+ {8934, 9219}, {5133, 5493}, {26666, 26787}, {45324, 45630},
+ {34880, 35008}, {20823, 20920}, {39571, 39704}, {15523, 15869},
+ {4360, 4637}, {46199, 46384}, {35991, 36242}, {46852, 46931},
+ {39218, 39644}, {11785, 12029}, {27225, 27366}, {29820, 30097},
+ {36778, 37072}, {9871, 10255}, {51065, 51208}, {38775, 39102},
+ {39446, 39712}, {33856, 34083}, {28853, 29289}, {526, 666},
+ {37510, 37697}, {13455, 13855}, {25648, 25691}, {10694, 11041},
+ {26441, 26889}, {18821, 19058}, {3357, 3590}, {15915, 16276},
+ {37706, 37934}, {24970, 25281}, {43951, 44124}, {35874, 36128},
+};
+
+void test_ngtcp2_rob_push_random(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_rob rob;
+ int rv;
+ uint8_t data[512];
+ size_t i;
+
+ ngtcp2_rob_init(&rob, 1024 * 1024, mem);
+ for (i = 0; i < ngtcp2_arraylen(randkeys); ++i) {
+ rv = ngtcp2_rob_push(&rob, randkeys[i].begin, &data[0],
+ (size_t)ngtcp2_range_len(&randkeys[i]));
+
+ CU_ASSERT(0 == rv);
+ }
+
+ CU_ASSERT(51401 == ngtcp2_rob_first_gap_offset(&rob));
+
+ ngtcp2_rob_free(&rob);
+}
+
+void test_ngtcp2_rob_data_at(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_rob rob;
+ int rv;
+ uint8_t data[256];
+ size_t i;
+ const uint8_t *p;
+ size_t len;
+ ngtcp2_rob_data *d;
+ ngtcp2_ksl_it it;
+ ngtcp2_rob_gap *g;
+
+ for (i = 0; i < sizeof(data); ++i) {
+ data[i] = (uint8_t)i;
+ }
+
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 3, &data[3], 13);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 0);
+
+ CU_ASSERT(0 == len);
+
+ rv = ngtcp2_rob_push(&rob, 0, &data[0], 3);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 0);
+
+ CU_ASSERT(16 == len);
+
+ for (i = 0; i < len; ++i) {
+ CU_ASSERT((uint8_t)i == *(p + i));
+ }
+
+ ngtcp2_rob_pop(&rob, 0, len);
+
+ rv = ngtcp2_rob_push(&rob, 16, &data[16], 5);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 16);
+
+ CU_ASSERT(5 == len);
+
+ for (i = 16; i < len; ++i) {
+ CU_ASSERT((uint8_t)i == *(p + i));
+ }
+
+ ngtcp2_rob_free(&rob);
+
+ /* Verify the case where data spans over multiple chunks */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 0, &data[0], 47);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 0);
+
+ CU_ASSERT(16 == len);
+
+ ngtcp2_rob_pop(&rob, 0, len);
+ len = ngtcp2_rob_data_at(&rob, &p, 16);
+
+ CU_ASSERT(16 == len);
+
+ ngtcp2_rob_pop(&rob, 16, len);
+ len = ngtcp2_rob_data_at(&rob, &p, 32);
+
+ CU_ASSERT(15 == len);
+
+ ngtcp2_rob_pop(&rob, 32, len);
+ ngtcp2_rob_free(&rob);
+
+ /* Verify the case where new offset comes before the existing
+ chunk */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 17, &data[17], 2);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 0);
+
+ CU_ASSERT(0 == len);
+
+ rv = ngtcp2_rob_push(&rob, 0, &data[0], 3);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 0);
+
+ CU_ASSERT(3 == len);
+
+ ngtcp2_rob_pop(&rob, 0, len);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 3);
+
+ CU_ASSERT(0 == len);
+
+ ngtcp2_rob_free(&rob);
+
+ /* Verify the case where new offset comes after the existing
+ chunk */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 0, &data[0], 3);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_rob_push(&rob, 16, &data[16], 32);
+
+ CU_ASSERT(0 == rv);
+
+ it = ngtcp2_ksl_begin(&rob.dataksl);
+ ngtcp2_ksl_it_next(&it);
+ d = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(16 == d->range.begin);
+
+ ngtcp2_ksl_it_next(&it);
+ d = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(32 == d->range.begin);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_rob_free(&rob);
+
+ /* Severely scattered data */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ for (i = 0; i < sizeof(data); i += 2) {
+ rv = ngtcp2_rob_push(&rob, i, &data[i], 1);
+
+ CU_ASSERT(0 == rv);
+ }
+
+ for (i = 1; i < sizeof(data); i += 2) {
+ rv = ngtcp2_rob_push(&rob, i, &data[i], 1);
+
+ CU_ASSERT(0 == rv);
+ }
+
+ for (i = 0; i < sizeof(data) / 16; ++i) {
+ len = ngtcp2_rob_data_at(&rob, &p, i * 16);
+
+ CU_ASSERT(16 == len);
+
+ ngtcp2_rob_pop(&rob, i * 16, len);
+ }
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(256 == g->range.begin);
+
+ it = ngtcp2_ksl_begin(&rob.dataksl);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_rob_free(&rob);
+
+ /* Verify the case where chunk is reused if it is not fully used */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 0, &data[0], 5);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 0);
+
+ CU_ASSERT(5 == len);
+
+ ngtcp2_rob_pop(&rob, 0, len);
+
+ rv = ngtcp2_rob_push(&rob, 2, &data[2], 8);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 5);
+
+ CU_ASSERT(5 == len);
+
+ ngtcp2_rob_pop(&rob, 5, len);
+
+ ngtcp2_rob_free(&rob);
+
+ /* Verify the case where 2nd push covers already processed region */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 0, &data[0], 16);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 0);
+
+ CU_ASSERT(16 == len);
+
+ ngtcp2_rob_pop(&rob, 0, len);
+
+ rv = ngtcp2_rob_push(&rob, 0, &data[0], 32);
+
+ CU_ASSERT(0 == rv);
+
+ len = ngtcp2_rob_data_at(&rob, &p, 16);
+
+ CU_ASSERT(16 == len);
+
+ ngtcp2_rob_pop(&rob, 16, len);
+
+ ngtcp2_rob_free(&rob);
+}
+
+void test_ngtcp2_rob_remove_prefix(void) {
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_rob rob;
+ ngtcp2_rob_gap *g;
+ ngtcp2_rob_data *d;
+ ngtcp2_ksl_it it;
+ uint8_t data[256];
+ int rv;
+
+ /* Removing data which spans multiple chunks */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 1, &data[1], 32);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_rob_remove_prefix(&rob, 33);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(33 == g->range.begin);
+
+ it = ngtcp2_ksl_begin(&rob.dataksl);
+ d = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(32 == d->range.begin);
+
+ ngtcp2_rob_free(&rob);
+
+ /* Remove an entire gap */
+ ngtcp2_rob_init(&rob, 16, mem);
+
+ rv = ngtcp2_rob_push(&rob, 1, &data[1], 3);
+
+ CU_ASSERT(0 == rv);
+
+ rv = ngtcp2_rob_push(&rob, 5, &data[5], 2);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_rob_remove_prefix(&rob, 16);
+
+ it = ngtcp2_ksl_begin(&rob.gapksl);
+ g = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(16 == g->range.begin);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_rob_free(&rob);
+}
diff --git a/tests/ngtcp2_rob_test.h b/tests/ngtcp2_rob_test.h
new file mode 100644
index 0000000..e1f54b6
--- /dev/null
+++ b/tests/ngtcp2_rob_test.h
@@ -0,0 +1,37 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ROB_TEST_H
+#define NGTCP2_ROB_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_rob_push(void);
+void test_ngtcp2_rob_push_random(void);
+void test_ngtcp2_rob_data_at(void);
+void test_ngtcp2_rob_remove_prefix(void);
+
+#endif /* NGTCP2_ROB_TEST_H */
diff --git a/tests/ngtcp2_rtb_test.c b/tests/ngtcp2_rtb_test.c
new file mode 100644
index 0000000..7c91c38
--- /dev/null
+++ b/tests/ngtcp2_rtb_test.c
@@ -0,0 +1,470 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rtb_test.h"
+
+#include <assert.h>
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_rtb.h"
+#include "ngtcp2_test_helper.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_pkt.h"
+
+static void conn_stat_init(ngtcp2_conn_stat *cstat) {
+ memset(cstat, 0, sizeof(*cstat));
+ cstat->max_tx_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+}
+
+void test_ngtcp2_rtb_add(void) {
+ ngtcp2_rtb rtb;
+ ngtcp2_rtb_entry *ent;
+ int rv;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_pkt_hd hd;
+ ngtcp2_log log;
+ ngtcp2_cid dcid;
+ ngtcp2_ksl_it it;
+ ngtcp2_conn_stat cstat;
+ ngtcp2_cc cc;
+ ngtcp2_strm crypto;
+ const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_HANDSHAKE;
+ ngtcp2_rst rst;
+ ngtcp2_objalloc frc_objalloc;
+ ngtcp2_objalloc rtb_entry_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+ ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem);
+
+ ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+ dcid_init(&dcid);
+ conn_stat_init(&cstat);
+ ngtcp2_rst_init(&rst);
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
+ 1000000007, 1, NGTCP2_PROTO_VER_V1, 0);
+
+ rv = ngtcp2_rtb_entry_objalloc_new(
+ &ent, &hd, NULL, 10, 0, NGTCP2_RTB_ENTRY_FLAG_NONE, &rtb_entry_objalloc);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_rtb_add(&rtb, ent, &cstat);
+
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
+ 1000000008, 2, NGTCP2_PROTO_VER_V1, 0);
+
+ rv = ngtcp2_rtb_entry_objalloc_new(
+ &ent, &hd, NULL, 9, 0, NGTCP2_RTB_ENTRY_FLAG_NONE, &rtb_entry_objalloc);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_rtb_add(&rtb, ent, &cstat);
+
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
+ 1000000009, 4, NGTCP2_PROTO_VER_V1, 0);
+
+ rv = ngtcp2_rtb_entry_objalloc_new(
+ &ent, &hd, NULL, 11, 0, NGTCP2_RTB_ENTRY_FLAG_NONE, &rtb_entry_objalloc);
+
+ CU_ASSERT(0 == rv);
+
+ ngtcp2_rtb_add(&rtb, ent, &cstat);
+
+ it = ngtcp2_rtb_head(&rtb);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ /* Check the top of the queue */
+ CU_ASSERT(1000000009 == ent->hd.pkt_num);
+
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(1000000008 == ent->hd.pkt_num);
+
+ ngtcp2_ksl_it_next(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ CU_ASSERT(1000000007 == ent->hd.pkt_num);
+
+ ngtcp2_ksl_it_next(&it);
+
+ CU_ASSERT(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+ ngtcp2_strm_free(&crypto);
+
+ ngtcp2_objalloc_free(&rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
+
+static void add_rtb_entry_range(ngtcp2_rtb *rtb, int64_t base_pkt_num,
+ size_t len, ngtcp2_conn_stat *cstat,
+ ngtcp2_objalloc *objalloc) {
+ ngtcp2_pkt_hd hd;
+ ngtcp2_rtb_entry *ent;
+ size_t i;
+ ngtcp2_cid dcid;
+
+ dcid_init(&dcid);
+
+ for (i = 0; i < len; ++i) {
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
+ base_pkt_num + (int64_t)i, 1, NGTCP2_PROTO_VER_V1, 0);
+ ngtcp2_rtb_entry_objalloc_new(&ent, &hd, NULL, 0, 0,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, objalloc);
+ ngtcp2_rtb_add(rtb, ent, cstat);
+ }
+}
+
+static void setup_rtb_fixture(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat,
+ ngtcp2_objalloc *objalloc) {
+ /* 100, ..., 154 */
+ add_rtb_entry_range(rtb, 100, 55, cstat, objalloc);
+ /* 180, ..., 184 */
+ add_rtb_entry_range(rtb, 180, 5, cstat, objalloc);
+ /* 440, ..., 446 */
+ add_rtb_entry_range(rtb, 440, 7, cstat, objalloc);
+}
+
+static void assert_rtb_entry_not_found(ngtcp2_rtb *rtb, int64_t pkt_num) {
+ ngtcp2_ksl_it it = ngtcp2_rtb_head(rtb);
+ ngtcp2_rtb_entry *ent;
+
+ for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+ CU_ASSERT(ent->hd.pkt_num != pkt_num);
+ }
+}
+
+void test_ngtcp2_rtb_recv_ack(void) {
+ ngtcp2_rtb rtb;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_max_frame mfr;
+ ngtcp2_ack *fr = &mfr.ackfr.ack;
+ ngtcp2_ack_range *ranges;
+ ngtcp2_log log;
+ ngtcp2_conn_stat cstat;
+ ngtcp2_cc cc;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_ssize num_acked;
+ ngtcp2_strm crypto;
+ const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_HANDSHAKE;
+ ngtcp2_rst rst;
+ ngtcp2_objalloc frc_objalloc;
+ ngtcp2_objalloc rtb_entry_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+ ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem);
+
+ ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, NULL, NULL, 0,
+ 1, NGTCP2_PROTO_VER_V1, 0);
+
+ /* no ack block */
+ conn_stat_init(&cstat);
+ ngtcp2_rst_init(&rst);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+ setup_rtb_fixture(&rtb, &cstat, &rtb_entry_objalloc);
+
+ CU_ASSERT(67 == ngtcp2_ksl_len(&rtb.ents));
+
+ fr->largest_ack = 446;
+ fr->first_ack_range = 1;
+ fr->rangecnt = 0;
+
+ num_acked =
+ ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
+
+ CU_ASSERT(2 == num_acked);
+ CU_ASSERT(65 == ngtcp2_ksl_len(&rtb.ents));
+ assert_rtb_entry_not_found(&rtb, 446);
+ assert_rtb_entry_not_found(&rtb, 445);
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+
+ /* with ack block */
+ conn_stat_init(&cstat);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+ setup_rtb_fixture(&rtb, &cstat, &rtb_entry_objalloc);
+
+ fr->largest_ack = 441;
+ fr->first_ack_range = 3; /* (441), (440), 439, 438 */
+ fr->rangecnt = 2;
+ ranges = fr->ranges;
+ ranges[0].gap = 253;
+ ranges[0].len = 0; /* (183) */
+ ranges[1].gap = 1; /* 182, 181 */
+ ranges[1].len = 1; /* (180), 179 */
+
+ num_acked =
+ ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
+
+ CU_ASSERT(4 == num_acked);
+ CU_ASSERT(63 == ngtcp2_ksl_len(&rtb.ents));
+ CU_ASSERT(441 == rtb.largest_acked_tx_pkt_num);
+ assert_rtb_entry_not_found(&rtb, 441);
+ assert_rtb_entry_not_found(&rtb, 440);
+ assert_rtb_entry_not_found(&rtb, 183);
+ assert_rtb_entry_not_found(&rtb, 180);
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+
+ /* gap+len points to pkt_num 0 */
+ conn_stat_init(&cstat);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+ add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc);
+
+ fr->largest_ack = 250;
+ fr->first_ack_range = 0;
+ fr->rangecnt = 1;
+ fr->ranges[0].gap = 248;
+ fr->ranges[0].len = 0;
+
+ num_acked =
+ ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
+
+ CU_ASSERT(1 == num_acked);
+ assert_rtb_entry_not_found(&rtb, 0);
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+
+ /* pkt_num = 0 (first ack block) */
+ conn_stat_init(&cstat);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+ add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc);
+
+ fr->largest_ack = 0;
+ fr->first_ack_range = 0;
+ fr->rangecnt = 0;
+
+ num_acked =
+ ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
+
+ CU_ASSERT(1 == num_acked);
+ assert_rtb_entry_not_found(&rtb, 0);
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+
+ /* pkt_num = 0 */
+ conn_stat_init(&cstat);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+ add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc);
+
+ fr->largest_ack = 2;
+ fr->first_ack_range = 0;
+ fr->rangecnt = 1;
+ fr->ranges[0].gap = 0;
+ fr->ranges[0].len = 0;
+
+ num_acked =
+ ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
+
+ CU_ASSERT(1 == num_acked);
+ assert_rtb_entry_not_found(&rtb, 0);
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+ ngtcp2_strm_free(&crypto);
+
+ ngtcp2_objalloc_free(&rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
+
+void test_ngtcp2_rtb_lost_pkt_ts(void) {
+ ngtcp2_rtb rtb;
+ const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APPLICATION;
+ ngtcp2_strm crypto;
+ ngtcp2_log log;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_cc cc;
+ ngtcp2_rst rst;
+ ngtcp2_conn_stat cstat;
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_objalloc frc_objalloc;
+ ngtcp2_objalloc rtb_entry_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+ ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem);
+
+ ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+
+ conn_stat_init(&cstat);
+ ngtcp2_rst_init(&rst);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+
+ add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc);
+
+ CU_ASSERT(UINT64_MAX == ngtcp2_rtb_lost_pkt_ts(&rtb));
+
+ it = ngtcp2_ksl_end(&rtb.ents);
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = 16777217;
+
+ CU_ASSERT(16777217 == ngtcp2_rtb_lost_pkt_ts(&rtb));
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+ ngtcp2_strm_free(&crypto);
+
+ ngtcp2_objalloc_free(&rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
+
+void test_ngtcp2_rtb_remove_expired_lost_pkt(void) {
+ ngtcp2_rtb rtb;
+ const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APPLICATION;
+ ngtcp2_strm crypto;
+ ngtcp2_log log;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_cc cc;
+ ngtcp2_rst rst;
+ ngtcp2_conn_stat cstat;
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+ size_t i;
+ ngtcp2_objalloc frc_objalloc;
+ ngtcp2_objalloc rtb_entry_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+ ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem);
+
+ ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+
+ conn_stat_init(&cstat);
+ ngtcp2_rst_init(&rst);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+
+ add_rtb_entry_range(&rtb, 0, 7, &cstat, &rtb_entry_objalloc);
+
+ it = ngtcp2_ksl_end(&rtb.ents);
+
+ for (i = 0; i < 5; ++i) {
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = 16777217 + i;
+ }
+
+ ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777219);
+
+ CU_ASSERT(5 == ngtcp2_ksl_len(&rtb.ents));
+
+ ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777223);
+
+ CU_ASSERT(2 == ngtcp2_ksl_len(&rtb.ents));
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+ ngtcp2_strm_free(&crypto);
+
+ ngtcp2_objalloc_free(&rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
+
+void test_ngtcp2_rtb_remove_excessive_lost_pkt(void) {
+ ngtcp2_rtb rtb;
+ const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APPLICATION;
+ ngtcp2_strm crypto;
+ ngtcp2_log log;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_cc cc;
+ ngtcp2_rst rst;
+ ngtcp2_conn_stat cstat;
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+ size_t i;
+ ngtcp2_objalloc frc_objalloc;
+ ngtcp2_objalloc rtb_entry_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+ ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem);
+
+ ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+ ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+
+ conn_stat_init(&cstat);
+ ngtcp2_rst_init(&rst);
+ ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+ ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL,
+ &rtb_entry_objalloc, &frc_objalloc, mem);
+
+ add_rtb_entry_range(&rtb, 0, 7, &cstat, &rtb_entry_objalloc);
+
+ it = ngtcp2_ksl_end(&rtb.ents);
+
+ for (i = 0; i < 5; ++i) {
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = 16777217;
+ ++rtb.num_lost_pkts;
+ }
+
+ ngtcp2_rtb_remove_excessive_lost_pkt(&rtb, 2);
+
+ CU_ASSERT(4 == ngtcp2_ksl_len(&rtb.ents));
+
+ ngtcp2_rtb_free(&rtb);
+ ngtcp2_cc_reno_cc_free(&cc, mem);
+ ngtcp2_strm_free(&crypto);
+
+ ngtcp2_objalloc_free(&rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
diff --git a/tests/ngtcp2_rtb_test.h b/tests/ngtcp2_rtb_test.h
new file mode 100644
index 0000000..f880e1e
--- /dev/null
+++ b/tests/ngtcp2_rtb_test.h
@@ -0,0 +1,38 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RTB_TEST_H
+#define NGTCP2_RTB_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_rtb_add(void);
+void test_ngtcp2_rtb_recv_ack(void);
+void test_ngtcp2_rtb_lost_pkt_ts(void);
+void test_ngtcp2_rtb_remove_expired_lost_pkt(void);
+void test_ngtcp2_rtb_remove_excessive_lost_pkt(void);
+
+#endif /* NGTCP2_RTB_TEST_H */
diff --git a/tests/ngtcp2_str_test.c b/tests/ngtcp2_str_test.c
new file mode 100644
index 0000000..3d5619d
--- /dev/null
+++ b/tests/ngtcp2_str_test.c
@@ -0,0 +1,96 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_str_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_test_helper.h"
+
+void test_ngtcp2_encode_ipv4(void) {
+ uint8_t buf[16];
+
+ CU_ASSERT(0 == strcmp("192.168.0.1",
+ (const char *)ngtcp2_encode_ipv4(
+ buf, (const uint8_t *)"\xc0\xa8\x00\x01")));
+ CU_ASSERT(0 ==
+ strcmp("127.0.0.1", (const char *)ngtcp2_encode_ipv4(
+ buf, (const uint8_t *)"\x7f\x00\x00\x01")));
+}
+
+void test_ngtcp2_encode_ipv6(void) {
+ uint8_t buf[32 + 7 + 1];
+
+ CU_ASSERT(
+ 0 ==
+ strcmp("2001:db8::2:1",
+ (const char *)ngtcp2_encode_ipv6(
+ buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x02\x00\x01")));
+ CU_ASSERT(
+ 0 ==
+ strcmp("2001:db8:0:1:1:1:1:1",
+ (const char *)ngtcp2_encode_ipv6(
+ buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x01\x00"
+ "\x01\x00\x01\x00\x01\x00\x01")));
+ CU_ASSERT(
+ 0 ==
+ strcmp("2001:db8::1:0:0:1",
+ (const char *)ngtcp2_encode_ipv6(
+ buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x00\x00"
+ "\x01\x00\x00\x00\x00\x00\x01")));
+ CU_ASSERT(
+ 0 ==
+ strcmp("2001:db8::8:800:200c:417a",
+ (const char *)ngtcp2_encode_ipv6(
+ buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x00\x00"
+ "\x08\x08\x00\x20\x0C\x41\x7a")));
+ CU_ASSERT(
+ 0 ==
+ strcmp("ff01::101",
+ (const char *)ngtcp2_encode_ipv6(
+ buf, (const uint8_t *)"\xff\x01\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x01\x01")));
+ CU_ASSERT(
+ 0 ==
+ strcmp("::1",
+ (const char *)ngtcp2_encode_ipv6(
+ buf, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x01")));
+ CU_ASSERT(
+ 0 ==
+ strcmp("::",
+ (const char *)ngtcp2_encode_ipv6(
+ buf, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00")));
+}
+
+void test_ngtcp2_get_bytes(void) {
+ const uint8_t src[] = {'f', 'o', 'o', 'b', 'a', 'r'};
+ uint8_t dest[256];
+
+ CU_ASSERT(src + sizeof(src) == ngtcp2_get_bytes(dest, src, sizeof(src)));
+ CU_ASSERT(0 == memcmp(src, dest, sizeof(src)));
+}
diff --git a/tests/ngtcp2_str_test.h b/tests/ngtcp2_str_test.h
new file mode 100644
index 0000000..9d67c9f
--- /dev/null
+++ b/tests/ngtcp2_str_test.h
@@ -0,0 +1,36 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_STR_TEST_H
+#define NGTCP2_STR_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_encode_ipv4(void);
+void test_ngtcp2_encode_ipv6(void);
+void test_ngtcp2_get_bytes(void);
+
+#endif /* NGTCP2_STR_TEST_H */
diff --git a/tests/ngtcp2_strm_test.c b/tests/ngtcp2_strm_test.c
new file mode 100644
index 0000000..b2a71c0
--- /dev/null
+++ b/tests/ngtcp2_strm_test.c
@@ -0,0 +1,575 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_strm_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_strm.h"
+#include "ngtcp2_test_helper.h"
+#include "ngtcp2_vec.h"
+
+static uint8_t nulldata[1024];
+
+static void setup_strm_streamfrq_fixture(ngtcp2_strm *strm,
+ ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_vec *data;
+
+ ngtcp2_strm_init(strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 2, frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 0;
+ frc->fr.stream.datacnt = 2;
+ data = frc->fr.stream.data;
+ data[0].len = 11;
+ data[0].base = nulldata;
+ data[1].len = 19;
+ data[1].base = nulldata + 11;
+
+ ngtcp2_strm_streamfrq_push(strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 2, frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 30;
+ frc->fr.stream.datacnt = 2;
+ data = frc->fr.stream.data;
+ data[0].len = 17;
+ data[0].base = nulldata + 30;
+ data[1].len = 29;
+ data[1].base = nulldata + 30 + 17;
+
+ ngtcp2_strm_streamfrq_push(strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 2, frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 76;
+ frc->fr.stream.datacnt = 2;
+ data = frc->fr.stream.data;
+ data[0].len = 31;
+ data[0].base = nulldata + 256;
+ data[1].len = 1;
+ data[1].base = nulldata + 512;
+
+ ngtcp2_strm_streamfrq_push(strm, frc);
+}
+
+void test_ngtcp2_strm_streamfrq_pop(void) {
+ ngtcp2_strm strm;
+ ngtcp2_frame_chain *frc;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ int rv;
+ ngtcp2_vec *data;
+ ngtcp2_objalloc frc_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+
+ /* Get first chain */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 30);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2 == frc->fr.stream.datacnt);
+
+ data = frc->fr.stream.data;
+
+ CU_ASSERT(11 == data[0].len);
+ CU_ASSERT(19 == data[1].len);
+ CU_ASSERT(2 == ngtcp2_ksl_len(strm.tx.streamfrq));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* Get merged chain */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 76);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2 == frc->fr.stream.datacnt);
+
+ data = frc->fr.stream.data;
+
+ CU_ASSERT(11 == data[0].len);
+ CU_ASSERT(19 + 46 == data[1].len);
+ CU_ASSERT(1 == ngtcp2_ksl_len(strm.tx.streamfrq));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* Get merged chain partially */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 75);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2 == frc->fr.stream.datacnt);
+
+ data = frc->fr.stream.data;
+
+ CU_ASSERT(11 == data[0].len);
+ CU_ASSERT(19 + 45 == data[1].len);
+ CU_ASSERT(2 == ngtcp2_ksl_len(strm.tx.streamfrq));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(75 == frc->fr.stream.offset);
+ CU_ASSERT(1 == frc->fr.stream.datacnt);
+ CU_ASSERT(1 == frc->fr.stream.data[0].len);
+ CU_ASSERT(nulldata + 30 + 17 + 28 == frc->fr.stream.data[0].base);
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* Not continuous merge */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 77);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(3 == frc->fr.stream.datacnt);
+
+ data = frc->fr.stream.data;
+
+ CU_ASSERT(11 == data[0].len);
+ CU_ASSERT(19 + 46 == data[1].len);
+ CU_ASSERT(1 == data[2].len);
+ CU_ASSERT(nulldata + 256 == data[2].base);
+ CU_ASSERT(1 == ngtcp2_ksl_len(strm.tx.streamfrq));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(77 == frc->fr.stream.offset);
+ CU_ASSERT(2 == frc->fr.stream.datacnt);
+
+ data = frc->fr.stream.data;
+
+ CU_ASSERT(30 == data[0].len);
+ CU_ASSERT(nulldata + 256 + 1 == data[0].base);
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* split; continuous */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 12);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == frc->fr.stream.offset);
+ CU_ASSERT(2 == frc->fr.stream.datacnt);
+
+ data = frc->fr.stream.data;
+
+ CU_ASSERT(11 == data[0].len);
+ CU_ASSERT(nulldata == data[0].base);
+ CU_ASSERT(1 == data[1].len);
+ CU_ASSERT(nulldata + 11 == data[1].base);
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(12 == frc->fr.stream.offset);
+ CU_ASSERT(3 == frc->fr.stream.datacnt);
+
+ data = frc->fr.stream.data;
+
+ CU_ASSERT(64 == data[0].len);
+ CU_ASSERT(nulldata + 12 == data[0].base);
+ CU_ASSERT(31 == data[1].len);
+ CU_ASSERT(nulldata + 256 == data[1].base);
+ CU_ASSERT(1 == data[2].len);
+ CU_ASSERT(nulldata + 512 == data[2].base);
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* offset gap */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 0;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 11;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 30;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 17;
+ data[0].base = nulldata + 30;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == frc->fr.stream.datacnt);
+ CU_ASSERT(11 == frc->fr.stream.data[0].len);
+ CU_ASSERT(1 == ngtcp2_ksl_len(strm.tx.streamfrq));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* fin */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 0;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 11;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 0, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 1;
+ frc->fr.stream.offset = 11;
+ frc->fr.stream.datacnt = 0;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == frc->fr.stream.fin);
+ CU_ASSERT(1 == frc->fr.stream.datacnt);
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* left == 0 and there is outstanding data */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == frc);
+
+ ngtcp2_strm_free(&strm);
+
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
+
+void test_ngtcp2_strm_streamfrq_unacked_offset(void) {
+ ngtcp2_strm strm;
+ ngtcp2_frame_chain *frc;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_vec *data;
+ ngtcp2_objalloc frc_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+
+ /* Everything acknowledged including fin */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_FIN_ACKED, 0, 0, NULL,
+ &frc_objalloc, mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 0;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 17;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 1;
+ frc->fr.stream.offset = 443;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 971;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_strm_ack_data(&strm, 0, 443 + 971);
+
+ CU_ASSERT((uint64_t)-1 == ngtcp2_strm_streamfrq_unacked_offset(&strm));
+
+ ngtcp2_strm_free(&strm);
+
+ /* Everything acknowledged but fin */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 0;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 17;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 1;
+ frc->fr.stream.offset = 443;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 971;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_strm_ack_data(&strm, 0, 443 + 971);
+
+ CU_ASSERT(443 + 971 == ngtcp2_strm_streamfrq_unacked_offset(&strm));
+
+ ngtcp2_strm_free(&strm);
+
+ /* Unacked gap starts in the middle of stream to resend */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 0;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 971;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_strm_ack_data(&strm, 0, 443);
+
+ CU_ASSERT(443 == ngtcp2_strm_streamfrq_unacked_offset(&strm));
+
+ ngtcp2_strm_free(&strm);
+
+ /* Unacked gap starts after stream to resend */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 0;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 971;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_strm_ack_data(&strm, 0, 971);
+
+ CU_ASSERT((uint64_t)-1 == ngtcp2_strm_streamfrq_unacked_offset(&strm));
+
+ ngtcp2_strm_free(&strm);
+
+ /* Unacked gap and stream overlap and gap starts before stream */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 977;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 971;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_strm_ack_data(&strm, 0, 971);
+
+ CU_ASSERT(977 == ngtcp2_strm_streamfrq_unacked_offset(&strm));
+
+ ngtcp2_strm_free(&strm);
+
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
+
+void test_ngtcp2_strm_streamfrq_unacked_pop(void) {
+ ngtcp2_strm strm;
+ ngtcp2_frame_chain *frc;
+ const ngtcp2_mem *mem = ngtcp2_mem_default();
+ ngtcp2_vec *data;
+ int rv;
+ ngtcp2_objalloc frc_objalloc;
+
+ ngtcp2_objalloc_init(&frc_objalloc, 1024, mem);
+
+ /* Everything acknowledged including fin */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_FIN_ACKED, 0, 0, NULL,
+ &frc_objalloc, mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 307;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 149;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 1;
+ frc->fr.stream.offset = 457;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 307;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_strm_ack_data(&strm, 0, 764);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL == frc);
+
+ ngtcp2_strm_free(&strm);
+
+ /* Everything acknowledged but fin */
+ ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc,
+ mem);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.offset = 307;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 149;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem);
+ frc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ frc->fr.stream.fin = 1;
+ frc->fr.stream.offset = 457;
+ frc->fr.stream.datacnt = 1;
+ data = frc->fr.stream.data;
+ data[0].len = 307;
+ data[0].base = nulldata;
+
+ ngtcp2_strm_streamfrq_push(&strm, frc);
+
+ ngtcp2_strm_ack_data(&strm, 0, 764);
+
+ frc = NULL;
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_FRAME_STREAM == frc->fr.type);
+ CU_ASSERT(1 == frc->fr.stream.fin);
+ CU_ASSERT(764 == frc->fr.stream.offset);
+ CU_ASSERT(0 == ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* Remove leading acknowledged data */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ ngtcp2_strm_ack_data(&strm, 0, 12);
+
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 43);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_FRAME_STREAM == frc->fr.type);
+ CU_ASSERT(0 == frc->fr.stream.fin);
+ CU_ASSERT(12 == frc->fr.stream.offset);
+ CU_ASSERT(1 == frc->fr.stream.datacnt);
+ CU_ASSERT(43 == ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ /* Creating a gap of acknowledged data */
+ setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem);
+
+ ngtcp2_strm_ack_data(&strm, 32, 1);
+
+ rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 43);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NGTCP2_FRAME_STREAM == frc->fr.type);
+ CU_ASSERT(0 == frc->fr.stream.fin);
+ CU_ASSERT(0 == frc->fr.stream.offset);
+ CU_ASSERT(2 == frc->fr.stream.datacnt);
+ CU_ASSERT(32 == ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt));
+
+ ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem);
+ ngtcp2_strm_free(&strm);
+
+ ngtcp2_objalloc_free(&frc_objalloc);
+}
diff --git a/tests/ngtcp2_strm_test.h b/tests/ngtcp2_strm_test.h
new file mode 100644
index 0000000..f94c423
--- /dev/null
+++ b/tests/ngtcp2_strm_test.h
@@ -0,0 +1,36 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_STRM_TEST_H
+#define NGTCP2_STRM_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_strm_streamfrq_pop(void);
+void test_ngtcp2_strm_streamfrq_unacked_offset(void);
+void test_ngtcp2_strm_streamfrq_unacked_pop(void);
+
+#endif /* NGTCP2_STRM_TEST_H */
diff --git a/tests/ngtcp2_test_helper.c b/tests/ngtcp2_test_helper.c
new file mode 100644
index 0000000..a29a943
--- /dev/null
+++ b/tests/ngtcp2_test_helper.c
@@ -0,0 +1,404 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_test_helper.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_conv.h"
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_ppe.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_net.h"
+
+size_t ngtcp2_t_encode_stream_frame(uint8_t *out, uint8_t flags,
+ uint64_t stream_id, uint64_t offset,
+ uint16_t datalen) {
+ uint8_t *p = out;
+
+ if (offset) {
+ flags |= NGTCP2_STREAM_OFF_BIT;
+ }
+ *p++ = NGTCP2_FRAME_STREAM | flags;
+
+ p = ngtcp2_put_uvarint(p, stream_id);
+
+ if (offset) {
+ p = ngtcp2_put_uvarint(p, offset);
+ }
+
+ if (flags & NGTCP2_STREAM_LEN_BIT) {
+ p = ngtcp2_put_uvarint(p, datalen);
+ }
+
+ memset(p, 0, datalen);
+ p += datalen;
+
+ return (size_t)(p - out);
+}
+
+size_t ngtcp2_t_encode_ack_frame(uint8_t *out, uint64_t largest_ack,
+ uint64_t first_ack_blklen, uint64_t gap,
+ uint64_t ack_blklen) {
+ uint8_t *p = out;
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_ACK;
+ /* Largest Acknowledged */
+ p = ngtcp2_put_uvarint(p, largest_ack);
+ /* ACK Delay */
+ p = ngtcp2_put_uvarint(p, 0);
+ /* ACK Block Count */
+ p = ngtcp2_put_uvarint(p, 1);
+ /* First ACK Block */
+ p = ngtcp2_put_uvarint(p, first_ack_blklen);
+ /* Gap (1) */
+ p = ngtcp2_put_uvarint(p, gap);
+ /* Additional ACK Block (1) */
+ p = ngtcp2_put_uvarint(p, ack_blklen);
+
+ return (size_t)(p - out);
+}
+
+static int null_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)dest;
+ (void)aead;
+ (void)aead_ctx;
+ (void)plaintext;
+ (void)plaintextlen;
+ (void)nonce;
+ (void)noncelen;
+ (void)aad;
+ (void)aadlen;
+ memset(dest + plaintextlen, 0, NGTCP2_FAKE_AEAD_OVERHEAD);
+ return 0;
+}
+
+static int null_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ (void)hp;
+ (void)hp_ctx;
+ (void)sample;
+ memcpy(dest, NGTCP2_FAKE_HP_MASK, sizeof(NGTCP2_FAKE_HP_MASK) - 1);
+ return 0;
+}
+
+size_t write_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ int64_t pkt_num, ngtcp2_frame *fr, size_t frlen,
+ ngtcp2_crypto_km *ckm) {
+ return write_pkt_flags(out, outlen, NGTCP2_PKT_FLAG_NONE, dcid, pkt_num, fr,
+ frlen, ckm);
+}
+
+size_t write_pkt_flags(uint8_t *out, size_t outlen, uint8_t flags,
+ const ngtcp2_cid *dcid, int64_t pkt_num,
+ ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm) {
+ ngtcp2_crypto_cc cc;
+ ngtcp2_ppe ppe;
+ ngtcp2_pkt_hd hd;
+ int rv;
+ ngtcp2_ssize n;
+ size_t i;
+
+ memset(&cc, 0, sizeof(cc));
+ cc.encrypt = null_encrypt;
+ cc.hp_mask = null_hp_mask;
+ cc.ckm = ckm;
+ cc.aead.max_overhead = NGTCP2_FAKE_AEAD_OVERHEAD;
+
+ ngtcp2_pkt_hd_init(&hd, flags, NGTCP2_PKT_1RTT, dcid, NULL, pkt_num, 4,
+ NGTCP2_PROTO_VER_V1, 0);
+
+ ngtcp2_ppe_init(&ppe, out, outlen, &cc);
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ assert(0 == rv);
+
+ for (i = 0; i < frlen; ++i, ++fr) {
+ rv = ngtcp2_ppe_encode_frame(&ppe, fr);
+ assert(0 == rv);
+ }
+
+ n = ngtcp2_ppe_final(&ppe, NULL);
+ assert(n > 0);
+
+ return (size_t)n;
+}
+
+static size_t write_long_header_pkt_generic(
+ uint8_t *out, size_t outlen, uint8_t pkt_type, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, int64_t pkt_num, uint32_t version,
+ const uint8_t *token, size_t tokenlen, ngtcp2_frame *fr, size_t frlen,
+ ngtcp2_crypto_km *ckm) {
+ ngtcp2_crypto_cc cc;
+ ngtcp2_ppe ppe;
+ ngtcp2_pkt_hd hd;
+ int rv;
+ ngtcp2_ssize n;
+ size_t i;
+
+ memset(&cc, 0, sizeof(cc));
+ cc.encrypt = null_encrypt;
+ cc.hp_mask = null_hp_mask;
+ cc.ckm = ckm;
+ switch (pkt_type) {
+ case NGTCP2_PKT_INITIAL:
+ cc.aead.max_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD;
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ case NGTCP2_PKT_0RTT:
+ cc.aead.max_overhead = NGTCP2_FAKE_AEAD_OVERHEAD;
+ break;
+ default:
+ assert(0);
+ }
+
+ /* ngtcp2_pkt_encode_hd_long requires known QUIC version. If we
+ need to write unsupported version for testing purpose, just
+ pretend that it is QUIC v1 here and rewrite the version field
+ later. */
+ ngtcp2_pkt_hd_init(
+ &hd, NGTCP2_PKT_FLAG_LONG_FORM, pkt_type, dcid, scid, pkt_num, 4,
+ version != NGTCP2_PROTO_VER_V1 && version != NGTCP2_PROTO_VER_V2_DRAFT
+ ? NGTCP2_PROTO_VER_V1
+ : version,
+ 0);
+
+ hd.token.base = (uint8_t *)token;
+ hd.token.len = tokenlen;
+
+ ngtcp2_ppe_init(&ppe, out, outlen, &cc);
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ assert(0 == rv);
+ ngtcp2_put_uint32be(&out[1], version);
+
+ for (i = 0; i < frlen; ++i, ++fr) {
+ rv = ngtcp2_ppe_encode_frame(&ppe, fr);
+ assert(0 == rv);
+ }
+
+ n = ngtcp2_ppe_final(&ppe, NULL);
+ assert(n > 0);
+ return (size_t)n;
+}
+
+size_t write_initial_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, int64_t pkt_num,
+ uint32_t version, const uint8_t *token,
+ size_t tokenlen, ngtcp2_frame *fr, size_t frlen,
+ ngtcp2_crypto_km *ckm) {
+ return write_long_header_pkt_generic(out, outlen, NGTCP2_PKT_INITIAL, dcid,
+ scid, pkt_num, version, token, tokenlen,
+ fr, frlen, ckm);
+}
+
+size_t write_handshake_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, int64_t pkt_num,
+ uint32_t version, ngtcp2_frame *fr, size_t frlen,
+ ngtcp2_crypto_km *ckm) {
+ return write_long_header_pkt_generic(out, outlen, NGTCP2_PKT_HANDSHAKE, dcid,
+ scid, pkt_num, version, NULL, 0, fr,
+ frlen, ckm);
+}
+
+size_t write_0rtt_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, int64_t pkt_num, uint32_t version,
+ ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm) {
+ return write_long_header_pkt_generic(out, outlen, NGTCP2_PKT_0RTT, dcid, scid,
+ pkt_num, version, NULL, 0, fr, frlen,
+ ckm);
+}
+
+ngtcp2_strm *open_stream(ngtcp2_conn *conn, int64_t stream_id) {
+ ngtcp2_strm *strm;
+ int rv;
+ (void)rv;
+
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
+ assert(strm);
+
+ rv = ngtcp2_conn_init_stream(conn, strm, stream_id, NULL);
+ assert(0 == rv);
+
+ return strm;
+}
+
+size_t rtb_entry_length(const ngtcp2_rtb_entry *ent) {
+ size_t len = 0;
+
+ for (; ent; ent = ent->next) {
+ ++len;
+ }
+
+ return len;
+}
+
+void dcid_init(ngtcp2_cid *cid) {
+ static const uint8_t id[] = "\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xff";
+ ngtcp2_cid_init(cid, id, sizeof(id) - 1);
+}
+
+void scid_init(ngtcp2_cid *cid) {
+ static const uint8_t id[] = "\xee\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xee";
+ ngtcp2_cid_init(cid, id, sizeof(id) - 1);
+}
+
+void rcid_init(ngtcp2_cid *cid) {
+ static const uint8_t id[] = "\xdd\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xdd";
+ ngtcp2_cid_init(cid, id, sizeof(id) - 1);
+}
+
+uint64_t read_pkt_payloadlen(const uint8_t *pkt, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid) {
+ uint64_t len;
+
+ ngtcp2_get_uvarint(&len, &pkt[1 + 4 + 1 + dcid->datalen + 1 + scid->datalen]);
+
+ return len;
+}
+
+void write_pkt_payloadlen(uint8_t *pkt, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t payloadlen) {
+ assert(payloadlen < 1073741824);
+ ngtcp2_put_uvarint30(&pkt[1 + 4 + 1 + dcid->datalen + 1 + scid->datalen],
+ (uint32_t)payloadlen);
+}
+
+ngtcp2_ssize pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen) {
+ const uint8_t *p;
+ ngtcp2_ssize nread;
+
+ nread = ngtcp2_pkt_decode_hd_long(dest, pkt, pktlen);
+ if (nread < 0 || (!(dest->flags & NGTCP2_PKT_FLAG_LONG_FORM) &&
+ dest->type == NGTCP2_PKT_VERSION_NEGOTIATION)) {
+ return nread;
+ }
+
+ if ((size_t)nread == pktlen) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = pkt + nread;
+
+ dest->pkt_numlen = (size_t)(pkt[0] & NGTCP2_PKT_NUMLEN_MASK) + 1;
+ if (pktlen < (size_t)nread + dest->pkt_numlen) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dest->pkt_num = ngtcp2_get_pkt_num(p, dest->pkt_numlen);
+
+ return nread + (ngtcp2_ssize)dest->pkt_numlen;
+}
+
+ngtcp2_ssize pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen, size_t dcidlen) {
+ const uint8_t *p;
+ ngtcp2_ssize nread;
+
+ nread = ngtcp2_pkt_decode_hd_short(dest, pkt, pktlen, dcidlen);
+ if (nread < 0) {
+ return nread;
+ }
+
+ if ((size_t)nread == pktlen) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = pkt + nread;
+
+ dest->pkt_numlen = (size_t)(pkt[0] & NGTCP2_PKT_NUMLEN_MASK) + 1;
+ if (pktlen < (size_t)nread + dest->pkt_numlen) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dest->pkt_num = ngtcp2_get_pkt_num(p, dest->pkt_numlen);
+
+ return nread + (ngtcp2_ssize)dest->pkt_numlen;
+}
+
+ngtcp2_ssize pkt_decode_hd_short_mask(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen, size_t dcidlen) {
+ static const uint8_t mask[] = NGTCP2_FAKE_HP_MASK;
+ const uint8_t *p;
+ ngtcp2_ssize nread;
+ uint8_t hb;
+ uint8_t pkt_numbuf[4];
+ size_t i;
+
+ nread = ngtcp2_pkt_decode_hd_short(dest, pkt, pktlen, dcidlen);
+ if (nread < 0) {
+ return nread;
+ }
+
+ if ((size_t)nread == pktlen) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = pkt + nread;
+
+ hb = (uint8_t)(pkt[0] ^ (mask[0] & 0x1f));
+
+ dest->pkt_numlen = (size_t)(hb & NGTCP2_PKT_NUMLEN_MASK) + 1;
+ if (pktlen < (size_t)nread + dest->pkt_numlen) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ for (i = 0; i < dest->pkt_numlen; ++i) {
+ pkt_numbuf[i] = *(p + i) ^ mask[i + 1];
+ }
+
+ dest->pkt_num = ngtcp2_get_pkt_num(pkt_numbuf, dest->pkt_numlen);
+
+ return nread + (ngtcp2_ssize)dest->pkt_numlen;
+}
+
+static void addr_init(ngtcp2_sockaddr_in *dest, uint32_t addr, uint16_t port) {
+ memset(dest, 0, sizeof(*dest));
+
+ dest->sin_family = AF_INET;
+ dest->sin_port = ngtcp2_htons(port);
+ dest->sin_addr.s_addr = ngtcp2_htonl(addr);
+}
+
+void path_init(ngtcp2_path_storage *path, uint32_t local_addr,
+ uint16_t local_port, uint32_t remote_addr,
+ uint16_t remote_port) {
+ ngtcp2_sockaddr_in la, ra;
+
+ addr_init(&la, local_addr, local_port);
+ addr_init(&ra, remote_addr, remote_port);
+
+ ngtcp2_path_storage_init(path, (ngtcp2_sockaddr *)&la, sizeof(la),
+ (ngtcp2_sockaddr *)&ra, sizeof(ra), NULL);
+}
diff --git a/tests/ngtcp2_test_helper.h b/tests/ngtcp2_test_helper.h
new file mode 100644
index 0000000..894cdf3
--- /dev/null
+++ b/tests/ngtcp2_test_helper.h
@@ -0,0 +1,191 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_TEST_HELPER_H
+#define NGTCP2_TEST_HELPER_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_conn.h"
+
+/*
+ * strsize macro returns the length of string literal |S|.
+ */
+#define strsize(S) (sizeof(S) - 1)
+
+/*
+ * NGTCP2_APP_ERRxx is an application error code solely used in test
+ * code.
+ */
+#define NGTCP2_APP_ERR01 0xff01u
+#define NGTCP2_APP_ERR02 0xff02u
+
+/*
+ * NGTCP2_FAKE_AEAD_OVERHEAD is AEAD overhead used in unit tests.
+ * Because we use the same encryption/decryption function for both
+ * handshake and post handshake packets, we have to use AEAD overhead
+ * used in handshake packets.
+ */
+#define NGTCP2_FAKE_AEAD_OVERHEAD NGTCP2_INITIAL_AEAD_OVERHEAD
+
+/* NGTCP2_FAKE_HP_MASK is a header protection mask used in unit
+ tests. */
+#define NGTCP2_FAKE_HP_MASK "\x00\x00\x00\x00\x00"
+
+/*
+ * ngtcp2_t_encode_stream_frame encodes STREAM frame into |out| with
+ * the given parameters. If NGTCP2_STREAM_LEN_BIT is set in |flags|,
+ * |datalen| is encoded as Data Length, otherwise it is not written.
+ * To set FIN bit in wire format, set NGTCP2_STREAM_FIN_BIT in
+ * |flags|. This function expects that |out| has enough length to
+ * store entire STREAM frame, excluding the Stream Data.
+ *
+ * This function returns the number of bytes written to |out|.
+ */
+size_t ngtcp2_t_encode_stream_frame(uint8_t *out, uint8_t flags,
+ uint64_t stream_id, uint64_t offset,
+ uint16_t datalen);
+
+/*
+ * ngtcp2_t_encode_ack_frame encodes ACK frame into |out| with the
+ * given parameters. Currently, this function encodes 1 ACK Block
+ * Section. ACK Delay field is always 0.
+ *
+ * This function returns the number of bytes written to |out|.
+ */
+size_t ngtcp2_t_encode_ack_frame(uint8_t *out, uint64_t largest_ack,
+ uint64_t first_ack_blklen, uint64_t gap,
+ uint64_t ack_blklen);
+
+/*
+ * write_pkt_flags writes a QUIC packet containing frames pointed by
+ * |fr| of length |frlen| in |out| whose capacity is |outlen|. This
+ * function returns the number of bytes written.
+ */
+size_t write_pkt_flags(uint8_t *out, size_t outlen, uint8_t flags,
+ const ngtcp2_cid *dcid, int64_t pkt_num,
+ ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm);
+
+/*
+ * write_pkt is write_pkt_flags with flag = NGTCP2_PKT_FLAG_NONE.
+ */
+size_t write_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ int64_t pkt_num, ngtcp2_frame *fr, size_t frlen,
+ ngtcp2_crypto_km *ckm);
+
+/*
+ * write_handshake_pkt writes a QUIC Initial packet containing |frlen|
+ * frames pointed by |fr| into |out| whose capacity is |outlen|. This
+ * function returns the number of bytes written.
+ */
+size_t write_initial_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, int64_t pkt_num,
+ uint32_t version, const uint8_t *token,
+ size_t tokenlen, ngtcp2_frame *fr, size_t frlen,
+ ngtcp2_crypto_km *ckm);
+
+/*
+ * write_handshake_pkt writes a QUIC Handshake packet containing
+ * |frlen| frames pointed by |fr| into |out| whose capacity is
+ * |outlen|. This function returns the number of bytes written.
+ */
+size_t write_handshake_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, int64_t pkt_num,
+ uint32_t version, ngtcp2_frame *fr, size_t frlen,
+ ngtcp2_crypto_km *ckm);
+
+/*
+ * write_0rtt_pkt writes a QUIC 0RTT packet containing |frlen| frames
+ * pointed by |fr| into |out| whose capacity is |outlen|. This
+ * function returns the number of bytes written.
+ */
+size_t write_0rtt_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, int64_t pkt_num, uint32_t version,
+ ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm);
+
+/*
+ * open_stream opens new stream denoted by |stream_id|.
+ */
+ngtcp2_strm *open_stream(ngtcp2_conn *conn, int64_t stream_id);
+
+/*
+ * rtb_entry_length returns the length of elements pointed by |ent|
+ * list.
+ */
+size_t rtb_entry_length(const ngtcp2_rtb_entry *ent);
+
+void scid_init(ngtcp2_cid *cid);
+void dcid_init(ngtcp2_cid *cid);
+void rcid_init(ngtcp2_cid *cid);
+
+/*
+ * read_pkt_payloadlen reads long header payload length field from
+ * |pkt|.
+ */
+uint64_t read_pkt_payloadlen(const uint8_t *pkt, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid);
+
+/*
+ * write_pkt_payloadlen writes long header payload length field into
+ * |pkt|.
+ */
+void write_pkt_payloadlen(uint8_t *pkt, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t payloadlen);
+
+/*
+ * pkt_decode_hd_long decodes long packet header from |pkt| of length
+ * |pktlen|. This function assumes that header protection has been
+ * decrypted.
+ */
+ngtcp2_ssize pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen);
+
+/*
+ * pkt_decode_hd_short decodes long packet header from |pkt| of length
+ * |pktlen|. This function assumes that header protection has been
+ * decrypted.
+ */
+ngtcp2_ssize pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen, size_t dcidlen);
+
+/*
+ * pkt_decode_hd_short_mask decodes long packet header from |pkt| of
+ * length |pktlen|. NGTCP2_FAKE_HP_MASK is used to decrypt header
+ * protection.
+ */
+ngtcp2_ssize pkt_decode_hd_short_mask(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen, size_t dcidlen);
+
+/*
+ * path_init initializes |path| with the given arguments. They form
+ * IPv4 addresses.
+ */
+void path_init(ngtcp2_path_storage *path, uint32_t local_addr,
+ uint16_t local_port, uint32_t remote_addr, uint16_t remote_port);
+
+#endif /* NGTCP2_TEST_HELPER_H */
diff --git a/tests/ngtcp2_vec_test.c b/tests/ngtcp2_vec_test.c
new file mode 100644
index 0000000..16cfba4
--- /dev/null
+++ b/tests/ngtcp2_vec_test.c
@@ -0,0 +1,426 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_vec_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "ngtcp2_vec.h"
+#include "ngtcp2_test_helper.h"
+
+void test_ngtcp2_vec_split(void) {
+ uint8_t nulldata[1024];
+ ngtcp2_vec a[16], b[16];
+ size_t acnt, bcnt;
+ ngtcp2_ssize nsplit;
+
+ /* No split occurs */
+ acnt = 1;
+ a[0].len = 135;
+ a[0].base = nulldata;
+
+ bcnt = 0;
+ b[0].len = 0;
+ b[0].base = NULL;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 135, 16);
+
+ CU_ASSERT(0 == nsplit);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(135 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(0 == bcnt);
+ CU_ASSERT(0 == b[0].len);
+ CU_ASSERT(NULL == b[0].base);
+
+ /* Split once */
+ acnt = 1;
+ a[0].len = 135;
+ a[0].base = nulldata;
+
+ bcnt = 0;
+ b[0].len = 0;
+ b[0].base = NULL;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 87, 16);
+
+ CU_ASSERT(48 == nsplit);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(87 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(1 == bcnt);
+ CU_ASSERT(48 == b[0].len);
+ CU_ASSERT(nulldata + 87 == b[0].base);
+
+ /* Multiple a vector; split at ngtcp2_vec boundary */
+ acnt = 2;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+
+ bcnt = 0;
+ b[0].len = 0;
+ b[0].base = NULL;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 16);
+
+ CU_ASSERT(89 == nsplit);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(1 == bcnt);
+ CU_ASSERT(89 == b[0].len);
+ CU_ASSERT(nulldata + 33 == b[0].base);
+
+ /* Multiple a vector; not split at ngtcp2_vec boundary */
+ acnt = 3;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+ a[2].len = 211;
+ a[2].base = nulldata + 33 + 89;
+
+ bcnt = 0;
+ b[0].len = 0;
+ b[0].base = NULL;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 34, 16);
+
+ CU_ASSERT(88 + 211 == nsplit);
+ CU_ASSERT(2 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(1 == a[1].len);
+ CU_ASSERT(nulldata + 33 == a[1].base);
+ CU_ASSERT(2 == bcnt);
+ CU_ASSERT(88 == b[0].len);
+ CU_ASSERT(nulldata + 34 == b[0].base);
+ CU_ASSERT(211 == b[1].len);
+ CU_ASSERT(nulldata + 34 + 88 == b[1].base);
+
+ /* Multiple a vector; split at ngtcp2_vec boundary; continuous
+ data */
+ acnt = 2;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+
+ bcnt = 2;
+ b[0].len = 17;
+ b[0].base = nulldata + 33 + 89;
+ b[1].len = 3;
+ b[1].base = nulldata + 33 + 89 + 17;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 16);
+
+ CU_ASSERT(89 == nsplit);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(2 == bcnt);
+ CU_ASSERT(89 + 17 == b[0].len);
+ CU_ASSERT(nulldata + 33 == b[0].base);
+ CU_ASSERT(3 == b[1].len);
+ CU_ASSERT(nulldata + 33 + 89 + 17 == b[1].base);
+
+ /* Multiple a vector; not split at ngtcp2_vec boundary; continuous
+ data; nmove == 0 */
+ acnt = 2;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+
+ bcnt = 2;
+ b[0].len = 17;
+ b[0].base = nulldata + 33 + 89;
+ b[1].len = 3;
+ b[1].base = nulldata + 33 + 89 + 17;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 34, 16);
+
+ CU_ASSERT(88 == nsplit);
+ CU_ASSERT(2 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(1 == a[1].len);
+ CU_ASSERT(nulldata + 33 == a[1].base);
+ CU_ASSERT(2 == bcnt);
+ CU_ASSERT(88 + 17 == b[0].len);
+ CU_ASSERT(nulldata + 34 == b[0].base);
+ CU_ASSERT(3 == b[1].len);
+ CU_ASSERT(nulldata + 33 + 89 + 17 == b[1].base);
+
+ /* Multiple a vector; not split at ngtcp2_vec boundary; continuous
+ data */
+ acnt = 3;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+ a[2].len = 211;
+ a[2].base = nulldata + 33 + 89;
+
+ bcnt = 2;
+ b[0].len = 17;
+ b[0].base = nulldata + 33 + 89 + 211;
+ b[1].len = 3;
+ b[1].base = nulldata + 33 + 89 + 211 + 17;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 34, 16);
+
+ CU_ASSERT(88 + 211 == nsplit);
+ CU_ASSERT(2 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(1 == a[1].len);
+ CU_ASSERT(nulldata + 33 == a[1].base);
+ CU_ASSERT(3 == bcnt);
+ CU_ASSERT(88 == b[0].len);
+ CU_ASSERT(nulldata + 34 == b[0].base);
+ CU_ASSERT(211 + 17 == b[1].len);
+ CU_ASSERT(nulldata + 34 + 88 == b[1].base);
+ CU_ASSERT(3 == b[2].len);
+ CU_ASSERT(nulldata + 33 + 89 + 211 + 17 == b[2].base);
+
+ /* Multiple a vector; split at ngtcp2_vec boundary; not continuous
+ data */
+ acnt = 2;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+
+ bcnt = 2;
+ b[0].len = 17;
+ b[0].base = nulldata + 256;
+ b[1].len = 3;
+ b[1].base = nulldata + 256 + 17;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 16);
+
+ CU_ASSERT(89 == nsplit);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(3 == bcnt);
+ CU_ASSERT(89 == b[0].len);
+ CU_ASSERT(nulldata + 33 == b[0].base);
+ CU_ASSERT(17 == b[1].len);
+ CU_ASSERT(nulldata + 256 == b[1].base);
+ CU_ASSERT(3 == b[2].len);
+ CU_ASSERT(nulldata + 256 + 17 == b[2].base);
+
+ /* maxcnt exceeded; continuous */
+ acnt = 2;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+
+ bcnt = 1;
+ b[0].len = 17;
+ b[0].base = nulldata + 33 + 89;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 32, 1);
+
+ CU_ASSERT(-1 == nsplit);
+
+ /* maxcnt exceeded; not continuous */
+ acnt = 2;
+ a[0].len = 33;
+ a[0].base = nulldata;
+ a[1].len = 89;
+ a[1].base = nulldata + 33;
+
+ bcnt = 1;
+ b[0].len = 17;
+ b[0].base = nulldata + 256;
+
+ nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 1);
+
+ CU_ASSERT(-1 == nsplit);
+}
+
+void test_ngtcp2_vec_merge(void) {
+ uint8_t nulldata[1024];
+ ngtcp2_vec a[16], b[16];
+ size_t acnt, bcnt;
+ size_t nmerged;
+
+ /* Merge one ngtcp2_vec completely */
+ acnt = 1;
+ a[0].len = 33;
+ a[0].base = nulldata;
+
+ bcnt = 1;
+ b[0].len = 11;
+ b[0].base = nulldata + 33;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 11, 16);
+
+ CU_ASSERT(11 == nmerged);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(44 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(0 == bcnt);
+
+ /* Merge ngtcp2_vec partially */
+ acnt = 1;
+ a[0].len = 33;
+ a[0].base = nulldata;
+
+ bcnt = 1;
+ b[0].len = 11;
+ b[0].base = nulldata + 33;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 10, 16);
+
+ CU_ASSERT(10 == nmerged);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(43 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(1 == bcnt);
+ CU_ASSERT(1 == b[0].len);
+ CU_ASSERT(nulldata + 33 + 10 == b[0].base);
+
+ /* Merge one ngtcp2_vec completely; data is not continuous */
+ acnt = 1;
+ a[0].len = 33;
+ a[0].base = nulldata;
+
+ bcnt = 1;
+ b[0].len = 11;
+ b[0].base = nulldata + 256;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 11, 16);
+
+ CU_ASSERT(11 == nmerged);
+ CU_ASSERT(2 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(11 == a[1].len);
+ CU_ASSERT(nulldata + 256 == a[1].base);
+ CU_ASSERT(0 == bcnt);
+
+ /* Merge ngtcp2_vec partially; data is not continuous */
+ acnt = 1;
+ a[0].len = 33;
+ a[0].base = nulldata;
+
+ bcnt = 1;
+ b[0].len = 11;
+ b[0].base = nulldata + 256;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 10, 16);
+
+ CU_ASSERT(10 == nmerged);
+ CU_ASSERT(2 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(10 == a[1].len);
+ CU_ASSERT(nulldata + 256 == a[1].base);
+ CU_ASSERT(1 == bcnt);
+ CU_ASSERT(1 == b[0].len);
+ CU_ASSERT(nulldata + 256 + 10 == b[0].base);
+
+ /* Merge ends at the ngtcp2_vec boundary */
+ acnt = 1;
+ a[0].len = 33;
+ a[0].base = nulldata;
+
+ bcnt = 2;
+ b[0].len = 11;
+ b[0].base = nulldata + 256;
+ b[1].len = 19;
+ b[1].base = nulldata + 256 + 11;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 11, 16);
+
+ CU_ASSERT(11 == nmerged);
+ CU_ASSERT(2 == acnt);
+ CU_ASSERT(33 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(11 == a[1].len);
+ CU_ASSERT(nulldata + 256 == a[1].base);
+ CU_ASSERT(1 == bcnt);
+ CU_ASSERT(19 == b[0].len);
+ CU_ASSERT(nulldata + 256 + 11 == b[0].base);
+
+ /* Merge occurs at the last object */
+ acnt = 1;
+ a[0].len = 33;
+ a[0].base = nulldata;
+
+ bcnt = 2;
+ b[0].len = 11;
+ b[0].base = nulldata + 33;
+ b[1].len = 99;
+ b[1].base = nulldata + 33 + 11;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 100, 1);
+
+ CU_ASSERT(100 == nmerged);
+ CU_ASSERT(1 == acnt);
+ CU_ASSERT(133 == a[0].len);
+ CU_ASSERT(nulldata == a[0].base);
+ CU_ASSERT(1 == bcnt);
+ CU_ASSERT(10 == b[0].len);
+ CU_ASSERT(nulldata + 33 + 11 + 89 == b[0].base);
+
+ /* No merge occurs if object is full */
+ acnt = 1;
+ a[0].len = 33;
+ a[0].base = nulldata;
+
+ bcnt = 1;
+ b[0].len = 3;
+ b[0].base = nulldata + 100;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 3, 1);
+
+ CU_ASSERT(0 == nmerged);
+}
+
+void test_ngtcp2_vec_len_varint(void) {
+ CU_ASSERT(0 == ngtcp2_vec_len_varint(NULL, 0));
+
+#if SIZE_MAX == UINT64_MAX
+ {
+ ngtcp2_vec v[] = {{NULL, NGTCP2_MAX_VARINT}, {NULL, 1}};
+
+ CU_ASSERT(-1 == ngtcp2_vec_len_varint(v, ngtcp2_arraylen(v)));
+ }
+
+ {
+ ngtcp2_vec v[] = {{NULL, NGTCP2_MAX_VARINT - 1}, {NULL, 1}};
+
+ CU_ASSERT(NGTCP2_MAX_VARINT ==
+ ngtcp2_vec_len_varint(v, ngtcp2_arraylen(v)));
+ }
+#endif /* SIZE_MAX == UINT64_MAX */
+}
diff --git a/tests/ngtcp2_vec_test.h b/tests/ngtcp2_vec_test.h
new file mode 100644
index 0000000..56e5d54
--- /dev/null
+++ b/tests/ngtcp2_vec_test.h
@@ -0,0 +1,36 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_VEC_TEST_H
+#define NGTCP2_VEC_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_ngtcp2_vec_split(void);
+void test_ngtcp2_vec_merge(void);
+void test_ngtcp2_vec_len_varint(void);
+
+#endif /* NGTCP2_VEC_TEST_H */