From 78cf6d1e50d4ebe8fb51fddfbdb55d36a5798386 Mon Sep 17 00:00:00 2001 From: Wu Zhao Date: Wed, 24 May 2023 11:48:15 +0800 Subject: [PATCH] [+] QUIC datagram (RFC9221), sending datagrams and bytestreams on http3 connections (non-standard extensions), and bugfix (#307) *[!] fix vulnerabilities on parsing STREAM and CRYPTO frame; [!] fix 32-bit system overflow problems; [+] support http3 over datagram; [+] support http3 byte stream; [+] add copa congestion control algorithm; * [+] update contributor * [!] fix test case errors on ubuntu latest --- CMakeLists.txt | 7 +- CONTRIBUTING.md | 1 + cmake/CMakeLists.txt | 5 + demo/demo_client.c | 10 +- demo/demo_server.c | 11 +- include/xquic/xqc_errno.h | 11 +- include/xquic/xqc_http3.h | 304 ++- include/xquic/xquic.h | 302 ++- include/xquic/xquic_typedef.h | 19 + scripts/case_test.sh | 2180 ++++++++++++++++- scripts/xquic.lds | 25 + src/common/xqc_log.c | 15 +- src/common/xqc_log_event_callback.c | 7 + src/congestion_control/xqc_cubic.c | 15 +- src/congestion_control/xqc_cubic.h | 1 + src/congestion_control/xqc_unlimited_cc.c | 74 + src/congestion_control/xqc_unlimited_cc.h | 13 + src/http3/frame/xqc_h3_frame.c | 44 + src/http3/frame/xqc_h3_frame.h | 17 +- src/http3/frame/xqc_h3_frame_defs.h | 24 +- src/http3/xqc_h3_conn.c | 14 +- src/http3/xqc_h3_conn.h | 36 +- src/http3/xqc_h3_ctx.c | 26 +- src/http3/xqc_h3_defs.c | 7 + src/http3/xqc_h3_defs.h | 9 +- src/http3/xqc_h3_ext_bytestream.c | 725 ++++++ src/http3/xqc_h3_ext_bytestream.h | 58 + src/http3/xqc_h3_ext_dgram.c | 105 + src/http3/xqc_h3_ext_dgram.h | 12 + src/http3/xqc_h3_stream.c | 422 +++- src/http3/xqc_h3_stream.h | 15 +- src/tls/xqc_tls.c | 15 + src/tls/xqc_tls.h | 4 + .../scheduler/xqc_scheduler_backup.c | 22 +- src/transport/xqc_client.c | 8 +- src/transport/xqc_conn.c | 492 +++- src/transport/xqc_conn.h | 33 + src/transport/xqc_datagram.c | 543 ++++ src/transport/xqc_datagram.h | 37 + src/transport/xqc_engine.c | 25 +- src/transport/xqc_frame.c | 69 +- src/transport/xqc_frame.h | 6 +- src/transport/xqc_frame_parser.c | 95 +- src/transport/xqc_frame_parser.h | 12 +- src/transport/xqc_multipath.c | 107 +- src/transport/xqc_multipath.h | 2 + src/transport/xqc_pacing.c | 2 +- src/transport/xqc_packet_out.c | 59 +- src/transport/xqc_packet_out.h | 7 + src/transport/xqc_recv_record.c | 9 +- src/transport/xqc_send_ctl.c | 101 +- src/transport/xqc_send_ctl.h | 5 + src/transport/xqc_send_queue.h | 11 +- src/transport/xqc_stream.c | 14 +- src/transport/xqc_stream.h | 1 + src/transport/xqc_timer.c | 80 + src/transport/xqc_timer.h | 116 + src/transport/xqc_transport_params.c | 61 +- src/transport/xqc_transport_params.h | 12 + tests/CMakeLists.txt | 2 + tests/test_client.c | 1107 ++++++++- tests/test_server.c | 674 ++++- tests/unittest/main.c | 4 + tests/unittest/xqc_datagram_test.c | 49 + tests/unittest/xqc_datagram_test.h | 10 + tests/unittest/xqc_h3_ext_test.c | 87 + tests/unittest/xqc_h3_ext_test.h | 10 + tests/unittest/xqc_process_frame_test.c | 6 - 68 files changed, 8115 insertions(+), 296 deletions(-) create mode 100644 src/congestion_control/xqc_unlimited_cc.c create mode 100644 src/congestion_control/xqc_unlimited_cc.h create mode 100644 src/http3/xqc_h3_ext_bytestream.c create mode 100644 src/http3/xqc_h3_ext_bytestream.h create mode 100644 src/http3/xqc_h3_ext_dgram.c create mode 100644 src/http3/xqc_h3_ext_dgram.h create mode 100644 src/transport/xqc_datagram.c create mode 100644 src/transport/xqc_datagram.h create mode 100644 tests/unittest/xqc_datagram_test.c create mode 100644 tests/unittest/xqc_datagram_test.h create mode 100644 tests/unittest/xqc_h3_ext_test.c create mode 100644 tests/unittest/xqc_h3_ext_test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d675d9c12..e4c0a800d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,8 @@ set( "src/http3/qpack/xqc_ins.c" "src/http3/qpack/stable/xqc_stable.c" "src/http3/qpack/dtable/xqc_dtable.c" + "src/http3/xqc_h3_ext_dgram.c" + "src/http3/xqc_h3_ext_bytestream.c" ) # Transport source @@ -160,6 +162,7 @@ set( "src/transport/xqc_packet_parser.c" "src/transport/xqc_frame_parser.c" "src/transport/xqc_stream.c" + "src/transport/xqc_datagram.c" "src/transport/xqc_packet_out.c" "src/transport/xqc_packet_in.c" "src/transport/xqc_send_ctl.c" @@ -233,6 +236,8 @@ set( CONGESTION_CONTROL_SOURCES "src/congestion_control/xqc_cubic.c" "src/congestion_control/xqc_bbr.c" + "src/congestion_control/xqc_unlimited_cc.c" + "src/congestion_control/xqc_copa.c" "src/congestion_control/xqc_window_filter.c" "src/congestion_control/xqc_sample.c" ) @@ -259,7 +264,7 @@ endif() # xquic source set ( - XQC_SOURCE + XQC_SOURCE ${HTTP3_SOURCES} ${TRANSPORT_SOURCES} ${TLS_SOURCE} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 40cd59b57..8d53801d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -203,6 +203,7 @@ In no particular order, thanks to these excellent individuals who contributed co * 曾柯(毅丝) * 徐盟欣(象谦) * Bai Shi(白石) +* 周瑞琪(凼凼) * @chinsyo * @L1MeN9Yu * @flx413 diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 4e6b54fa2..2d4e51b95 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -149,6 +149,8 @@ set( "src/http3/qpack/xqc_ins.c" "src/http3/qpack/stable/xqc_stable.c" "src/http3/qpack/dtable/xqc_dtable.c" + "src/http3/xqc_h3_ext_dgram.c" + "src/http3/xqc_h3_ext_bytestream.c" ) # Transport source @@ -161,6 +163,7 @@ set( "src/transport/xqc_packet_parser.c" "src/transport/xqc_frame_parser.c" "src/transport/xqc_stream.c" + "src/transport/xqc_datagram.c" "src/transport/xqc_packet_out.c" "src/transport/xqc_packet_in.c" "src/transport/xqc_send_ctl.c" @@ -234,6 +237,8 @@ set( CONGESTION_CONTROL_SOURCES "src/congestion_control/xqc_cubic.c" "src/congestion_control/xqc_bbr.c" + "src/congestion_control/xqc_unlimited_cc.c" + "src/congestion_control/xqc_copa.c" "src/congestion_control/xqc_window_filter.c" "src/congestion_control/xqc_sample.c" ) diff --git a/demo/demo_client.c b/demo/demo_client.c index ab956df28..ea1880dd8 100644 --- a/demo/demo_client.c +++ b/demo/demo_client.c @@ -469,7 +469,7 @@ xqc_demo_cli_close_keylog_file(xqc_demo_cli_ctx_t *ctx) } void -xqc_demo_cli_keylog_cb(const char *line, void *engine_user_data) +xqc_demo_cli_keylog_cb(const xqc_cid_t *scid, const char *line, void *engine_user_data) { xqc_demo_cli_ctx_t *ctx = (xqc_demo_cli_ctx_t*)engine_user_data; if (ctx->keylog_fd <= 0) { @@ -585,6 +585,13 @@ xqc_demo_cli_write_socket(const unsigned char *buf, size_t size, const struct so return res; } +ssize_t +xqc_demo_cli_write_socket_ex(uint64_t path_id, const unsigned char *buf, size_t size, + const struct sockaddr *peer_addr, socklen_t peer_addrlen, void *conn_user_data) +{ + return xqc_demo_cli_write_socket(buf, size, peer_addr, peer_addrlen, conn_user_data); +} + #if defined(XQC_SUPPORT_SENDMMSG) ssize_t @@ -1638,6 +1645,7 @@ xqc_demo_cli_init_callback(xqc_engine_callback_t *cb, xqc_transport_callbacks_t static xqc_transport_callbacks_t tcb = { .write_socket = xqc_demo_cli_write_socket, + .write_socket_ex = xqc_demo_cli_write_socket_ex, .save_token = xqc_demo_cli_save_token, /* save token */ .save_session_cb = xqc_demo_cli_save_session_cb, .save_tp_cb = xqc_demo_cli_save_tp_cb, diff --git a/demo/demo_server.c b/demo/demo_server.c index f5143467e..0084482cb 100644 --- a/demo/demo_server.c +++ b/demo/demo_server.c @@ -276,6 +276,7 @@ int xqc_demo_svr_open_keylog_file(xqc_demo_svr_ctx_t *ctx) { ctx->keylog_fd = open(ctx->args->env_cfg.key_out_path, (O_WRONLY | O_APPEND | O_CREAT), 0644); + printf("%s %d\n", ctx->args->env_cfg.key_out_path, ctx->keylog_fd); if (ctx->keylog_fd <= 0) { return -1; } @@ -295,7 +296,7 @@ xqc_demo_svr_close_keylog_file(xqc_demo_svr_ctx_t *ctx) } void -xqc_demo_svr_keylog_cb(const char *line, void *eng_user_data) +xqc_demo_svr_keylog_cb(const xqc_cid_t *scid, const char *line, void *eng_user_data) { xqc_demo_svr_ctx_t *ctx = (xqc_demo_svr_ctx_t*)eng_user_data; if (ctx->keylog_fd <= 0) { @@ -928,6 +929,12 @@ xqc_demo_svr_write_socket(const unsigned char *buf, size_t size, const struct so return res; } +ssize_t +xqc_demo_svr_write_socket_ex(uint64_t path_id, const unsigned char *buf, size_t size, + const struct sockaddr *peer_addr,socklen_t peer_addrlen, void *conn_user_data) +{ + return xqc_demo_svr_write_socket(buf, size, peer_addr, peer_addrlen, conn_user_data); +} void xqc_demo_svr_socket_write_handler(xqc_demo_svr_ctx_t *ctx, int fd) @@ -1136,6 +1143,7 @@ xqc_demo_svr_init_args(xqc_demo_svr_args_t *args) args->env_cfg.log_level = XQC_LOG_DEBUG; strncpy(args->env_cfg.log_path, LOG_PATH, TLS_GROUPS_LEN - 1); strncpy(args->env_cfg.source_file_dir, SOURCE_DIR, RESOURCE_LEN - 1); + strncpy(args->env_cfg.key_out_path, KEY_PATH, PATH_LEN - 1); strncpy(args->env_cfg.priv_key_path, PRIV_KEY_PATH, PATH_LEN - 1); strncpy(args->env_cfg.cert_pem_path, CERT_PEM_PATH, PATH_LEN - 1); } @@ -1239,6 +1247,7 @@ xqc_demo_svr_init_callback(xqc_engine_callback_t *cb, xqc_transport_callbacks_t static xqc_transport_callbacks_t tcb = { .server_accept = xqc_demo_svr_accept, .write_socket = xqc_demo_svr_write_socket, + .write_socket_ex = xqc_demo_svr_write_socket_ex, .conn_update_cid_notify = xqc_demo_svr_conn_update_cid_notify, }; diff --git a/include/xquic/xqc_errno.h b/include/xquic/xqc_errno.h index c8beba4f0..cae778499 100644 --- a/include/xquic/xqc_errno.h +++ b/include/xquic/xqc_errno.h @@ -23,6 +23,7 @@ typedef enum { TRA_INVALID_TOKEN = 0xB, TRA_APPLICATION_ERROR = 0xC, TRA_CRYPTO_BUFFER_EXCEEDED = 0xD, + TRA_0RTT_TRANS_PARAMS_ERROR = 0xE, /* MUST delete the current saved 0RTT transport parameters */ TRA_HS_CERTIFICATE_VERIFY_FAIL = 0x1FE, /* for handshake certificate verify error */ TRA_CRYPTO_ERROR = 0x1FF, /* 0x1XX */ } xqc_trans_err_code_t; @@ -121,7 +122,10 @@ typedef enum { XQC_EENCRYPT_LB_CID = 670, /* load balance connection ID encryption error */ XQC_EENCRYPT_AES_128_ECB = 671, /* aes_128_ecb algorithm error */ - + + XQC_EDGRAM_NOT_SUPPORTED = 680, /* Datagram - not supported */ + XQC_EDGRAM_TOO_LARGE = 681, /* Datagram - payload size too large */ + XQC_E_MAX, } xqc_transport_error_t; @@ -216,6 +220,11 @@ typedef enum { XQC_H3_BLOCKED_STREAM_EXCEED = 825, /* blocked_stream exceed limit */ XQC_H3_STREAM_RECV_ERROR = 826, /* call xqc_stream_recv error */ XQC_H3_INVALID_PRIORITY = 827, /* invalid http priority params or values */ + XQC_H3_INVALID_BIDI_STREAM_TYPE = 828, /* invalid bidi stream type */ + XQC_H3_ECREATE_BYTESTREAM = 829, /* fail to create a bytestream */ + XQC_H3_EPROC_BYTESTREAM = 830, /* fail to process bytestream */ + XQC_H3_BYTESTREAM_FIN_SENT = 831, /* try to send data on a bytestream that already sent FIN */ + XQC_H3_BYTESTREAM_MSG_BUF_EXIST = 832, /* try to create a msg buf while it already exists */ XQC_H3_ERR_MAX, } xqc_h3_error_t; diff --git a/include/xquic/xqc_http3.h b/include/xquic/xqc_http3.h index 5f4eabb3a..cbe94fe8a 100644 --- a/include/xquic/xqc_http3.h +++ b/include/xquic/xqc_http3.h @@ -139,17 +139,37 @@ typedef struct xqc_request_stats_s { /** * @brief 请求级别MP状态 - * 0: 该请求所在连接未成功建立起双路 (validated_path_count <= 1) - * 1: 该请求是通过2条路径传输(发包or收包 任意方向,包括重注入数据) (validated_path_count > 1 && aggregate_cnt > 1) - * 2: 该请求所在连接有成功建立起双路,但请求在单条路径上传输 (validated_path_count > 1 && aggregate_cnt <= 1) + * 0: 该请求所在连接当前仅有一条可用路径 + * 1: 该请求所在连接当前有多条可用路径,该请求同时在 Available 和 Standby 路径传输 + * 2: 该请求所在连接当前有多条可用路径,但该请求仅在 Standby 路径传输 + * 3: 该请求所在连接当前有多条可用路径,但该请求仅在 Available 路径传输 */ int mp_state; float mp_default_path_send_weight; float mp_default_path_recv_weight; + float mp_standby_path_send_weight; + float mp_standby_path_recv_weight; char stream_info[XQC_STREAM_INFO_LEN]; } xqc_request_stats_t; +/** + * @brief bytestream statistics + * + */ +typedef struct xqc_h3_ext_bytestream_stats_s { + size_t bytes_sent; + size_t bytes_rcvd; + int stream_err; + const char *stream_close_msg; + xqc_usec_t create_time; + xqc_usec_t fin_rcvd_time; + xqc_usec_t fin_read_time; + xqc_usec_t fin_sent_time; + xqc_usec_t fin_acked_time; + xqc_usec_t first_byte_sent_time; + xqc_usec_t first_byte_rcvd_time; +} xqc_h3_ext_bytestream_stats_t; /* connection settings for http3 */ typedef struct xqc_h3_conn_settings_s { @@ -175,7 +195,89 @@ typedef struct xqc_h3_conn_settings_s { } xqc_h3_conn_settings_t; +/** + * @brief callback for h3 bytestream read + * @param h3_ext_bs bytestream + * @param data data to be read. NOTE, this could be a NULL pointer, please ONLY read it if data_len > 0. + * @param data_len length of data to be read + * @param fin the bytestream is finished + * @param bs_user_data bytestream user data + * @param data_recv_time time spent for receiving data + */ +typedef int (*xqc_h3_ext_bytestream_read_notify_pt)(xqc_h3_ext_bytestream_t *h3_ext_bs, + const void *data, size_t data_len, uint8_t fin, void *bs_user_data, uint64_t data_recv_time); + +/** + * @brief callbacks for extended h3 bytestream + */ +typedef int (*xqc_h3_ext_bytestream_notify_pt)(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *bs_user_data); + + +/** + * @brief the callback API to notify the application that there is a datagram to be read + * + * @param conn the connection handle + * @param user_data the user_data set by xqc_h3_ext_datagram_set_user_data + * @param data the data delivered by this callback + * @param data_len the length of the delivered data + * @param data_recv_time time spent for receiving data + */ +typedef void (*xqc_h3_ext_datagram_read_notify_pt)(xqc_h3_conn_t *conn, + const void *data, size_t data_len, void *user_data, uint64_t data_recv_time); + +/** + * @brief the callback API to notify the application that datagrams can be sent + * + * @param conn the connection handle + * @param user_data the user_data set by xqc_h3_ext_datagram_set_user_data + */ +typedef void (*xqc_h3_ext_datagram_write_notify_pt)(xqc_h3_conn_t *conn, + void *user_data); + +/** + * @brief the callback API to notify the application that a datagram is declared lost. + * However, the datagram could also be acknowledged later, as the underlying + * loss detection is not fully accurate. Applications should handle this type of + * spurious loss. The return value is used to ask the QUIC stack to retransmit the lost + * datagram packet. + * + * @param conn the connection handle + * @param user_data the user_data set by xqc_h3_ext_datagram_set_user_data + * @param dgram_id the id of the lost datagram + * @return 0, do not retransmit; + * XQC_DGRAM_RETX_ASKED_BY_APP, retransmit; + * others, ignored by the QUIC stack. + */ +typedef int (*xqc_h3_ext_datagram_lost_notify_pt)(xqc_h3_conn_t *conn, + uint64_t dgram_id, void *user_data); + +/** + * @brief the callback API to notify the application that a datagram is acked + * + * @param conn the connection handle + * @param user_data the user_data set by xqc_h3_ext_datagram_set_user_data + * @param dgram_id the id of the acked datagram + */ +typedef void (*xqc_h3_ext_datagram_acked_notify_pt)(xqc_h3_conn_t *conn, + uint64_t dgram_id, void *user_data); + + +typedef struct xqc_h3_ext_dgram_callbacks_s { + + /* the return value is ignored by XQUIC stack */ + xqc_h3_ext_datagram_read_notify_pt dgram_read_notify; + + /* the return value is ignored by XQUIC stack */ + xqc_h3_ext_datagram_write_notify_pt dgram_write_notify; + /* the return value is ignored by XQUIC stack */ + xqc_h3_ext_datagram_acked_notify_pt dgram_acked_notify; + + /* the return value is ignored by XQUIC stack */ + xqc_h3_ext_datagram_lost_notify_pt dgram_lost_notify; + +} xqc_h3_ext_dgram_callbacks_t; /** * @brief http3 connection callbacks for application layer @@ -215,14 +317,36 @@ typedef struct xqc_h3_request_callbacks_s { } xqc_h3_request_callbacks_t; +typedef struct xqc_h3_ext_bytestream_callbacks_s { + + /* the return value is ignored by XQUIC stack */ + xqc_h3_ext_bytestream_notify_pt bs_create_notify; + + /* the return value is ignored by XQUIC stack */ + xqc_h3_ext_bytestream_notify_pt bs_close_notify; + + /* negative return values will cause the connection to be closed */ + xqc_h3_ext_bytestream_read_notify_pt bs_read_notify; + + /* negative return values will cause the connection to be closed */ + xqc_h3_ext_bytestream_notify_pt bs_write_notify; + +} xqc_h3_ext_bytestream_callbacks_t; + typedef struct xqc_h3_callbacks_s { /* http3 connection callbacks */ - xqc_h3_conn_callbacks_t h3c_cbs; + xqc_h3_conn_callbacks_t h3c_cbs; /* http3 request callbacks */ - xqc_h3_request_callbacks_t h3r_cbs; + xqc_h3_request_callbacks_t h3r_cbs; + + /* datagram callbacks */ + xqc_h3_ext_dgram_callbacks_t h3_ext_dgram_cbs; + + /* bytestream callbacks */ + xqc_h3_ext_bytestream_callbacks_t h3_ext_bs_cbs; } xqc_h3_callbacks_t; @@ -348,6 +472,16 @@ xqc_int_t xqc_h3_conn_get_errno(xqc_h3_conn_t *h3c); XQC_EXPORT_PUBLIC_API void xqc_h3_conn_set_user_data(xqc_h3_conn_t *h3c, void *user_data); +/** + * @brief get user_data for http3 connection, user_data could be the application layer context of + * http3 connection + * + * @param h3c handler of http3 connection + * @return user_data + */ +XQC_EXPORT_PUBLIC_API +void* xqc_h3_conn_get_user_data(xqc_h3_conn_t *h3_conn); + /** * User can set h3 settings when h3_conn_create_notify callbacks @@ -554,6 +688,166 @@ XQC_EXPORT_PUBLIC_API xqc_int_t xqc_h3_request_set_priority(xqc_h3_request_t *h3r, xqc_h3_priority_t *prio); +/****************************/ +/* New APIs for extended H3 */ +/****************************/ + +/** + * @brief create a bytestream based on extended H3 + * @param engine handler created by xqc_engine_create + * @param cid connection id of http3 connection + * @param user_data For bytestream + * @return handler of bytestream + */ +XQC_EXPORT_PUBLIC_API +xqc_h3_ext_bytestream_t *xqc_h3_ext_bytestream_create(xqc_engine_t *engine, + const xqc_cid_t *cid, void *user_data); + +/** + * @brief close bytestream, send QUIC RESET_STREAM frame to peer. h3_ext_bytestream_close_notify will + * triggered when bytestream is finally destroyed + * + * @param xqc_h3_ext_bytestream_t handler of bytestream + * @return XQC_OK for success, others for error + */ +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_h3_ext_bytestream_close(xqc_h3_ext_bytestream_t *h3_ext_bs); + +/** + * @brief finish bytestream. if fin is not sent yet, and application has nothing to send anymore, call + * this function to send a QUIC STREAM frame with only fin + * + * @return > 0 for Bytes sent,-XQC_EAGAIN try next time, < 0 for error, 0 for bytestream finished + */ +XQC_EXPORT_PUBLIC_API +ssize_t xqc_h3_ext_bytestream_finish(xqc_h3_ext_bytestream_t *h3_ext_bs); + +/** + * @brief set user_data of a bytestream, which will be used as the parameter of the bytestream + * callback functions. server should set user_data when h3_ext_bytestream_create_notify triggers + * + * @param xqc_h3_ext_bytestream_t handler of the bytestream + * @param user_data user data of the bytestream callback functions + */ +XQC_EXPORT_PUBLIC_API +void xqc_h3_ext_bytestream_set_user_data(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *user_data); + + +/** + * @brief get the user data associcated with the bytestream object + * + * @param xqc_h3_ext_bytestream_t handler of the bytestream + * @param user_data user data of the bytestream callback functions + * @return the pointer of user data + */ +XQC_EXPORT_PUBLIC_API +void* xqc_h3_ext_bytestream_get_user_data(xqc_h3_ext_bytestream_t *h3_ext_bs); + +/** + * @brief get statistics of a bytestream + * + * @param xqc_h3_ext_bytestream_t handler of the bytestream + * @return statistics information of the bytestream + */ +XQC_EXPORT_PUBLIC_API +xqc_h3_ext_bytestream_stats_t xqc_h3_ext_bytestream_get_stats( + xqc_h3_ext_bytestream_t *h3_ext_bs); + +/** + * @brief send data + * + * @param xqc_h3_ext_bytestream_t handler of the bytestream + * @param data content + * @param data_size data length + * @param fin request finish flag, 1 for finish. + * @param qos level (must be the values defined in xqc_data_qos_level_t) + * @return > 0 for bytes sent,-XQC_EAGAIN try next time, < 0 for error, 0 for bytestream finished + */ +XQC_EXPORT_PUBLIC_API +ssize_t xqc_h3_ext_bytestream_send(xqc_h3_ext_bytestream_t *h3_ext_bs, + unsigned char *data, size_t data_size, uint8_t fin, + xqc_data_qos_level_t qos_level); + +/** + * @brief Get QUIC stream ID by a bytestream + * + * @param xqc_h3_ext_bytestream_t handler of a bytestream + * @return QUIC stream id + */ +XQC_EXPORT_PUBLIC_API +xqc_stream_id_t xqc_h3_ext_bytestream_id(xqc_h3_ext_bytestream_t *h3_ext_bs); + +/** + * @brief get the h3 connection associated with a bytestream + * + * @param xqc_h3_ext_bytestream_t handler of a bytestream + * @return an h3 connection + */ +XQC_EXPORT_PUBLIC_API +xqc_h3_conn_t *xqc_h3_ext_bytestream_get_h3_conn( + xqc_h3_ext_bytestream_t *h3_ext_bs); + +/** + * @brief the API to get the max length of the data that can be sent + * via a single call of xqc_datagram_send + * + * @param conn the connection handle + * @return 0 = the peer does not support datagram, >0 = the max length + */ +XQC_EXPORT_PUBLIC_API +size_t xqc_h3_ext_datagram_get_mss(xqc_h3_conn_t *conn); + +/** + * Server should set datagram user_data when datagram callbacks + * @dgram_data: the user_data of all datagram callbacks + */ +XQC_EXPORT_PUBLIC_API +void xqc_h3_ext_datagram_set_user_data(xqc_h3_conn_t *conn, void *user_data); + +/** + * @return the user_data of all datagram callbacks + */ +XQC_EXPORT_PUBLIC_API +void* xqc_h3_ext_datagram_get_user_data(xqc_h3_conn_t *conn); + + +/** + * @brief the API to send a datagram over the h3 connection + * + * @param conn the connection handle + * @param data the data to be sent + * @param data_len the length of the data + * @param *dgram_id the pointer to return the id the datagram + * @param qos level (must be the values defined in xqc_data_qos_level_t) + * @return <0 = error (-XQC_EAGAIN, -XQC_CLOSING, -XQC_DGRAM_NOT_SUPPORTED, -XQC_DGRAM_TOO_LARGE, ...), + * 0 success + */ +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_h3_ext_datagram_send(xqc_h3_conn_t *conn, void *data, + size_t data_len, uint64_t *dgram_id, + xqc_data_qos_level_t qos_level); + +/** + * @brief the API to send a datagram over the h3 connection + * + * @param conn the connection handle + * @param iov multiple data buffers need to be sent + * @param *dgram_id the pointer to return the list of dgram_id + * @param iov_size the size of iov list + * @param *sent_cnt the number of successfully sent datagrams + * @param *sent_bytes the total bytes of successfully sent datagrams + * @param qos level (must be the values defined in xqc_data_qos_level_t) + * @return <0 = error (-XQC_EAGAIN, -XQC_CLOSING, -XQC_DGRAM_NOT_SUPPORTED, -XQC_DGRAM_TOO_LARGE, ...), + * 0 success + */ +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_h3_ext_datagram_send_multiple(xqc_h3_conn_t *conn, + struct iovec *iov, uint64_t *dgram_id_list, size_t iov_size, + size_t *sent_cnt, size_t *sent_bytes, + xqc_data_qos_level_t qos_level); + + #ifdef __cplusplus } #endif diff --git a/include/xquic/xquic.h b/include/xquic/xquic.h index b2d8f4e88..b9649cf87 100644 --- a/include/xquic/xquic.h +++ b/include/xquic/xquic.h @@ -68,6 +68,8 @@ typedef enum xqc_proto_version_s { #define XQC_INITIAL_PATH_ID 0 +#define XQC_DGRAM_RETX_ASKED_BY_APP 1 + /** * @brief get timestamp callback function. this might be useful on different platforms @@ -98,6 +100,14 @@ typedef void (*xqc_set_event_timer_pt)(xqc_usec_t wake_after, void *engine_user_ typedef ssize_t (*xqc_cid_generate_pt)(const xqc_cid_t *ori_cid, uint8_t *cid_buf, size_t cid_buflen, void *engine_user_data); +/** + * @brief engine secret log callback. will only be effective when build with XQC_PRINT_SECRET + * + * this callback will be invoked everytime when TLS layer generates a secret, and will be triggered + * multiple times during handshake. keylog could be used in wireshark to parse QUIC packets + */ +typedef void (*xqc_eng_keylog_pt)(const xqc_cid_t *scid, const char *line, void *engine_user_data); + /** * @brief tls secret log callback. will only be effective when build with XQC_PRINT_SECRET * @@ -231,6 +241,7 @@ typedef xqc_save_string_pt xqc_save_session_pt; */ typedef xqc_save_string_pt xqc_save_trans_param_pt; + /** * @brief handshake finished callback function * @@ -465,7 +476,60 @@ typedef ssize_t (*xqc_send_mmsg_ex_pt)(uint64_t path_id, * client, or the parameter of xqc_stream_set_user_data set by server * @return 0 for success, -1 for failure */ -typedef int (*xqc_stream_notify_pt)(xqc_stream_t *stream, void *strm_user_data); +typedef xqc_int_t (*xqc_stream_notify_pt)(xqc_stream_t *stream, + void *strm_user_data); + +/** + * @brief the callback API to notify application that there is a datagram to be read + * + * @param conn the connection handle + * @param user_data the dgram_data set by xqc_datagram_set_user_data + * @param data the data delivered by this callback + * @param data_len the length of the delivered data + * @param dgram_recv_ts the unix timestamp when the datagram is received from socket + */ +typedef void (*xqc_datagram_read_notify_pt)(xqc_connection_t *conn, + void *user_data, const void *data, size_t data_len, uint64_t unix_ts); + +/** + * @brief the callback API to notify application that datagrams can be sent + * + * @param conn the connection handle + * @param user_data the dgram_data set by xqc_datagram_set_user_data + */ +typedef void (*xqc_datagram_write_notify_pt)(xqc_connection_t *conn, + void *user_data); + +/** + * @brief the callback API to notify application that a datagram is declared lost. + * However, the datagram could also be acknowledged later, as the underlying + * loss detection is not fully accurate. Applications should handle this type of + * spurious loss. The return value indicates how this lost datagram is + * handled by the QUIC stack. NOTE, if the QUIC stack replicates the datagram + * (e.g. reinjection or retransmission), this callback can be triggered + * multiple times for a dgram_id. + * + * @param conn the connection handle + * @param user_data the dgram_data set by xqc_datagram_set_user_data + * @param dgram_id the id of the lost datagram + * @return 0: the stack will not retransmit the packet; + * XQC_DGRAM_RETX_ASKED_BY_APP (1): the stack will retransmit the packet; + * others are ignored by the QUIC stack. + */ +typedef xqc_int_t (*xqc_datagram_lost_notify_pt)(xqc_connection_t *conn, + uint64_t dgram_id, void *user_data); + +/** + * @brief the callback API to notify application that a datagram is acked. Note, + * for every unique dgram_id, this callback will be only called once. + * + * @param conn the connection handle + * @param user_data the dgram_data set by xqc_datagram_set_user_data + * @param dgram_id the id of the acked datagram + */ +typedef void (*xqc_datagram_acked_notify_pt)(xqc_connection_t *conn, + uint64_t dgram_id, void *user_data); + /** @@ -613,7 +677,7 @@ typedef struct xqc_conn_callbacks_s { * * return 0 for success, -1 for failure, e.g. malloc error, on which xquic will close connection */ - xqc_conn_notify_pt conn_create_notify; + xqc_conn_notify_pt conn_create_notify; /** * connection close notify. REQUIRED for both client and server @@ -621,17 +685,17 @@ typedef struct xqc_conn_callbacks_s { * this function will be invoked after QUIC connection is closed. user can free application * level context created in conn_create_notify callback function */ - xqc_conn_notify_pt conn_close_notify; + xqc_conn_notify_pt conn_close_notify; /** * handshake complete callback. OPTIONAL for client and server */ - xqc_handshake_finished_pt conn_handshake_finished; + xqc_handshake_finished_pt conn_handshake_finished; /** * active PING acked callback. OPTIONAL for both client and server */ - xqc_conn_ping_ack_notify_pt conn_ping_acked; + xqc_conn_ping_ack_notify_pt conn_ping_acked; } xqc_conn_callbacks_t; @@ -673,6 +737,40 @@ typedef struct xqc_stream_callbacks_s { } xqc_stream_callbacks_t; +/* QUIC layer datagram callback functions */ +typedef struct xqc_datagram_callbacks_s { + /** + * datagram read callback function. REQUIRED for both client and server if they want to use datagram + * + * this will be triggered when a QUIC datagram is received. application layer could read + * data from the arguments of this callback. + */ + xqc_datagram_read_notify_pt datagram_read_notify; + + /** + * datagram write callback function. REQUIRED for both client and server if they want to use datagram + * + * when sending data with xqc_datagram_send or xqc_datagram_send_multiple, xquic might be blocked or send part of the data. if + * this callback function is triggered, applications can continue to send the rest data. + */ + xqc_datagram_write_notify_pt datagram_write_notify; + + /** + * datagram acked callback function. OPTIONAL for server and client. + * + * this will be triggered when a QUIC packet containing a DATAGRAM frame is acked. + */ + xqc_datagram_acked_notify_pt datagram_acked_notify; + + /** + * datagram lost callback function. OPTIONAL for server and client. + * + * this will be triggered when a QUIC packet containing a DATAGRAM frame is lost. + */ + xqc_datagram_lost_notify_pt datagram_lost_notify; + +} xqc_datagram_callbacks_t; + /** * @brief connection and stream callbacks for QUIC level, Application-Layer-Protocol shall implement @@ -686,15 +784,36 @@ typedef struct xqc_app_proto_callbacks_s { /* QUIC stream callback functions */ xqc_stream_callbacks_t stream_cbs; + /* QUIC datagram callback functions */ + xqc_datagram_callbacks_t dgram_cbs; + } xqc_app_proto_callbacks_t; +typedef enum { + XQC_DATA_QOS_HIGHEST = 1, + XQC_DATA_QOS_HIGH = 2, + XQC_DATA_QOS_MEDIUM = 3, + XQC_DATA_QOS_NORMAL = 4, + XQC_DATA_QOS_LOW = 5, + XQC_DATA_QOS_LOWEST = 6, +} xqc_data_qos_level_t; typedef struct xqc_cc_params_s { uint32_t customize_on; uint32_t init_cwnd; + uint32_t min_cwnd; uint32_t expect_bw; uint32_t max_expect_bw; uint32_t cc_optimization_flags; + /* 0 < delta <= delta_max, default 0.05, ->0 = more throughput-oriented */ + double copa_delta_base; + /* 0 < delta_max <= 1.0, default 0.5 */ + double copa_delta_max; + /* + * 1.0 <= delta_ai_unit, default 1.0, greater values mean more aggressive + * when Copa competes with loss-based CCAs. + */ + double copa_delta_ai_unit; } xqc_cc_params_t; typedef struct xqc_scheduler_params_u { @@ -765,6 +884,8 @@ XQC_EXPORT_PUBLIC_API extern const xqc_cong_ctrl_callback_t xqc_bbr2_cb; #endif XQC_EXPORT_PUBLIC_API extern const xqc_cong_ctrl_callback_t xqc_bbr_cb; XQC_EXPORT_PUBLIC_API extern const xqc_cong_ctrl_callback_t xqc_cubic_cb; +XQC_EXPORT_PUBLIC_API extern const xqc_cong_ctrl_callback_t xqc_unlimited_cc_cb; +XQC_EXPORT_PUBLIC_API extern const xqc_cong_ctrl_callback_t xqc_copa_cb; typedef enum xqc_scheduler_path_event_e { XQC_SCHED_EVENT_PATH_NOT_FULL = 0, @@ -878,6 +999,12 @@ typedef struct xqc_config_s { * engine. if write_mmsg is NULL and sendmmsg_on is non-zero, xqc_engine_create will fail */ int sendmmsg_on; + + /** + * @brief enable h3 ext (default: 0) + * + */ + uint8_t enable_h3_ext; } xqc_config_t; @@ -895,7 +1022,7 @@ typedef struct xqc_engine_callback_s { xqc_cid_generate_pt cid_generate_cb; /* tls secret callback, OPTIONAL */ - xqc_keylog_pt keylog_cb; + xqc_eng_keylog_pt keylog_cb; /* get realtime timestamp callback function. if not set, xquic will get timestamp with inner function xqc_now, which relies on gettimeofday */ @@ -971,7 +1098,11 @@ typedef struct xqc_conn_settings_s { xqc_cong_ctrl_callback_t cong_ctrl_callback; /* default: xqc_cubic_cb */ xqc_cc_params_t cc_params; uint32_t so_sndbuf; /* socket option SO_SNDBUF, 0 for unlimited */ - uint64_t sndq_packets_used_max; /* default: XQC_SNDQ_PACKETS_USED_MAX */ + uint64_t sndq_packets_used_max; /* + * default: XQC_SNDQ_PACKETS_USED_MAX. + * It should be set to buffer 2xBDP packets at least for performance consideration. + * The default value is 16000 pkts. + */ xqc_linger_t linger; xqc_proto_version_t proto_version; /* QUIC protocol version */ xqc_msec_t init_idle_time_out; /* initial idle timeout interval, effective before handshake completion */ @@ -981,6 +1112,15 @@ typedef struct xqc_conn_settings_s { uint64_t keyupdate_pkt_threshold; /* packet limit of a single 1-rtt key, 0 for unlimited */ size_t max_pkt_out_size; + /* + * datgram option + * 0: no support for datagram mode (default) + * >0: the max size of datagrams that the local end is willing to receive + * 65535: the local end is willing to receive a datagram with any length as + * long as it fits in a QUIC packet + */ + uint16_t max_datagram_frame_size; + /* * multipath option: * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-02#section-3 @@ -1021,6 +1161,20 @@ typedef struct xqc_conn_settings_s { uint64_t path_unreachable_pto_count; xqc_msec_t standby_path_probe_timeout; + + /* params for performance tuning */ + /* max ack delay: ms */ + uint32_t max_ack_delay; + /* generate an ACK if received ack-eliciting pkts >= ack_frequency */ + uint32_t ack_frequency; + uint64_t loss_detection_pkt_thresh; + double pto_backoff_factor; + + /* datagram redundancy */ + uint8_t datagram_redundancy; + uint8_t datagram_force_retrans_on; + uint64_t datagram_redundant_probe; + } xqc_conn_settings_t; @@ -1055,6 +1209,7 @@ typedef struct xqc_conn_stats_s { uint32_t lost_count; uint32_t tlp_count; uint32_t spurious_loss_count; + uint32_t lost_dgram_count; /*how many datagram frames (pkts) are lost*/ xqc_usec_t srtt; xqc_0rtt_flag_t early_data_flag; uint32_t recv_count; @@ -1084,7 +1239,6 @@ typedef struct xqc_conn_stats_s { typedef struct xqc_path_stats_s { uint8_t get_stats_success; - xqc_usec_t last_tra_path_status_changed_time; uint32_t send_count_since_last_tra_path_status_changed; uint32_t pto_count_since_last_tra_path_status_changed; @@ -1204,6 +1358,15 @@ XQC_EXPORT_PUBLIC_API void xqc_engine_set_log_level(xqc_engine_t *engine, xqc_log_level_t log_level); +/** + * @brief enable/disable the log module of xquic + * + * @param enable XQC_TRUE for enable, XQC_FALSE for disable + */ +XQC_EXPORT_PUBLIC_API +void xqc_log_enable(xqc_bool_t enable); + + /** * user should call after a number of packet processed in xqc_engine_packet_process * call after recv a batch packets, may destroy connection when error @@ -1264,6 +1427,14 @@ XQC_EXPORT_PUBLIC_API xqc_int_t xqc_conn_get_errno(xqc_connection_t *conn); +/** + * @brief get latest rtt sample of the initial path + * + */ +XQC_EXPORT_PUBLIC_API +xqc_usec_t xqc_conn_get_lastest_rtt(xqc_engine_t *engine, const xqc_cid_t *cid); + + /** * Server should set user_data when conn_create_notify callbacks */ @@ -1324,6 +1495,35 @@ XQC_EXPORT_PUBLIC_API void xqc_conn_unset_pkt_filter_callback(xqc_connection_t *conn); +/** + * @brief get public local transport settings. + */ +XQC_EXPORT_PUBLIC_API +xqc_conn_public_local_trans_settings_t +xqc_conn_get_public_local_trans_settings(xqc_connection_t *conn); + +/** + * @brief set public local transport settings + */ +XQC_EXPORT_PUBLIC_API +void xqc_conn_set_public_local_trans_settings(xqc_connection_t *conn, + xqc_conn_public_local_trans_settings_t *settings); + +/** + * @brief get public remote transport settings. + */ +XQC_EXPORT_PUBLIC_API +xqc_conn_public_remote_trans_settings_t +xqc_conn_get_public_remote_trans_settings(xqc_connection_t *conn); + +/** + * @brief set public remote transport settings + */ +XQC_EXPORT_PUBLIC_API +void xqc_conn_set_public_remote_trans_settings(xqc_connection_t *conn, + xqc_conn_public_remote_trans_settings_t *settings); + + /** * Create new stream in quic connection. * @param user_data user_data for this stream @@ -1387,6 +1587,68 @@ XQC_EXPORT_PUBLIC_API ssize_t xqc_stream_send(xqc_stream_t *stream, unsigned char *send_data, size_t send_data_size, uint8_t fin); +/** + * @brief the API to get the max length of the data that can be sent + * via a single call of xqc_datagram_send; NOTE, if the DCID length could + * be changed during the lifetime of the connection, applications is + * suggested to call xqc_datagram_get_mss every time before + * send datagram data or when getting -XQC_EDGRAM_TOO_LARGE error + * from sending datagram data. In MPQUIC cases, the DCID of all paths + * MUST be the same. Otherwise, there might be unexpected errors. + * + * @param conn the connection handle + * @return 0 = the peer does not support datagram, >0 = the max length + */ +XQC_EXPORT_PUBLIC_API +size_t xqc_datagram_get_mss(xqc_connection_t *conn); + +/** + * Server should set datagram user_data when datagram callbacks + * @dgram_data: the user_data of all datagram callbacks + */ +XQC_EXPORT_PUBLIC_API +void xqc_datagram_set_user_data(xqc_connection_t *conn, void *dgram_data); + +/** + * @dgram_data: the user_data of all datagram callbacks + */ +XQC_EXPORT_PUBLIC_API +void* xqc_datagram_get_user_data(xqc_connection_t *conn); + +/** + * @brief the API to send a datagram over the QUIC connection + * + * @param conn the connection handle + * @param data the data to be sent + * @param data_len the length of the data + * @param *dgram_id the pointer to return the id the datagram + * @param qos level (must be the values defined in xqc_data_qos_level_t) + * @return <0 = error (-XQC_EAGAIN, -XQC_CLOSING, -XQC_EDGRAM_NOT_SUPPORTED, -XQC_EDGRAM_TOO_LARGE, ...), + * 0 success + */ +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_datagram_send(xqc_connection_t *conn, void *data, + size_t data_len, uint64_t *dgram_id, xqc_data_qos_level_t qos_level); + +/** + * @brief the API to send a datagram over the QUIC connection + * + * @param conn the connection handle + * @param iov multiple data buffers need to be sent + * @param *dgram_id the pointer to return the list of dgram_id + * @param iov_size the size of iov list + * @param *sent_cnt the number of successfully sent datagrams + * @param *sent_bytes the total bytes of successfully sent datagrams + * @param qos level (must be the values defined in xqc_data_qos_level_t) + * @return <0 = error (-XQC_EAGAIN, -XQC_CLOSING, -XQC_EDGRAM_NOT_SUPPORTED, -XQC_EDGRAM_TOO_LARGE, ...), + * 0 success + */ +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_datagram_send_multiple(xqc_connection_t *conn, + struct iovec *iov, uint64_t *dgram_id_list, size_t iov_size, + size_t *sent_cnt, size_t *sent_bytes, xqc_data_qos_level_t qos_level); + + /** * Get dcid and scid before process packet */ @@ -1425,6 +1687,12 @@ uint8_t xqc_engine_config_get_cid_len(xqc_engine_t *engine); XQC_EXPORT_PUBLIC_API xqc_int_t xqc_conn_continue_send(xqc_engine_t *engine, const xqc_cid_t *cid); +/** + * User should call xqc_conn_continue_send when write event ready + */ +XQC_EXPORT_PUBLIC_API +void xqc_conn_continue_send_by_conn(xqc_connection_t *conn); + /** * User can get xqc_conn_stats_t by cid */ @@ -1520,6 +1788,24 @@ xqc_path_stats_t xqc_path_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid, XQC_EXPORT_PUBLIC_API xqc_int_t xqc_lb_cid_encryption(uint8_t *cid_buf, size_t enc_len, uint8_t *out_buf, size_t out_buf_len, uint8_t *lb_cid_key, size_t lb_cid_key_len, xqc_engine_t *engine); +/** + * @brief client calls this API to check if it should delete 0rtt ticket according to + * the errorcode of xqc_conn in conn_close_notify + * @return XQC_TRUE = yes; + */ +XQC_EXPORT_PUBLIC_API +xqc_bool_t xqc_conn_should_clear_0rtt_ticket(xqc_int_t conn_err); + +/** + * @brief Users call this function to get a template of conn settings, which serves + * as the starting point for users who want to refine conn settings according + * to their needs + * @param settings_type there are different types of templates in XQUIC + * @return conn settings + */ +XQC_EXPORT_PUBLIC_API +xqc_conn_settings_t xqc_conn_get_conn_settings_template(xqc_conn_settings_type_t settings_type); + #ifdef __cplusplus } #endif diff --git a/include/xquic/xquic_typedef.h b/include/xquic/xquic_typedef.h index be5e3ec39..f594e86f3 100644 --- a/include/xquic/xquic_typedef.h +++ b/include/xquic/xquic_typedef.h @@ -50,6 +50,7 @@ typedef struct xqc_memory_pool_s xqc_memory_pool_t; typedef struct xqc_bbr_info_interface_s xqc_bbr_info_interface_t; typedef struct xqc_path_ctx_s xqc_path_ctx_t; typedef struct xqc_timer_manager_s xqc_timer_manager_t; +typedef struct xqc_h3_ext_bytestream_s xqc_h3_ext_bytestream_t; typedef uint64_t xqc_msec_t; /* store millisecond values */ typedef uint64_t xqc_usec_t; /* store microsecond values */ @@ -138,5 +139,23 @@ typedef struct xqc_http_priority_s { uint8_t reinject; } xqc_h3_priority_t; +/* ALPN definition */ +#define XQC_DEFINED_ALPN_H3 "h3" +#define XQC_DEFINED_ALPN_H3_29 "h3-29" +#define XQC_DEFINED_ALPN_H3_EXT "h3-ext" + +typedef enum xqc_conn_settings_type_e { + XQC_CONN_SETTINGS_DEFAULT, + XQC_CONN_SETTINGS_LOW_DELAY, +} xqc_conn_settings_type_t; + +typedef struct xqc_conn_public_local_trans_settings_s { + uint16_t max_datagram_frame_size; + uint8_t datagram_redundancy; +} xqc_conn_public_local_trans_settings_t; + +typedef struct xqc_conn_public_remote_trans_settings_s { + uint16_t max_datagram_frame_size; +} xqc_conn_public_remote_trans_settings_t; #endif /*_XQUIC_TYPEDEF_H_INCLUDED_*/ diff --git a/scripts/case_test.sh b/scripts/case_test.sh index 6e356819f..fe08faee6 100755 --- a/scripts/case_test.sh +++ b/scripts/case_test.sh @@ -5,6 +5,9 @@ #macOS #export EVENT_NOKQUEUE=1 +LOCAL_TEST=0 +#LOCAL_TEST=1 + cd ../build # start test_server @@ -35,6 +38,76 @@ function case_print_result() { } +clear_log +echo -e "log switch off ...\c" +./test_client -s 1024000 -l d -t 1 -E -x 44 >> stdlog +log_size=`wc -c clog | awk -F ' ' '{print $1}'` +if [ $log_size -eq 0 ]; then + echo ">>>>>>>> pass:1" + case_print_result "log_switch_off" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "log_switch_off" "fail" +fi + +rm -f test_session tp_localhost xqc_token + +killall test_server 2> /dev/null +./test_server -l d -e -x 99 > /dev/null & +sleep 1 + +clear_log +echo -e "stream send pure fin ...\c" +./test_client -s 1024 -l d -t 1 -E -x 99 -T 1 >> clog +errlog=`grep_err_log` +clog_res=`cat clog | grep "|send_state:3|recv_state:3|stream_id:0|stream_type:0|send_bytes:0|read_bytes:0|recv_bytes:0|stream_len:0|"` +slog_res=`cat slog | grep "|send_state:3|recv_state:3|stream_id:0|stream_type:0|send_bytes:0|read_bytes:0|recv_bytes:0|stream_len:0|"` +if [ -z "$errlog" ] && [ -n "$clog_res" ] && [ -n "$slog_res" ] ; then + echo ">>>>>>>> pass:1" + case_print_result "stream_send_pure_fin" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "stream_send_pure_fin" "fail" +fi + +rm -f test_session + +clear_log +echo -e "h3 stream send pure fin ...\c" +./test_client -s 1024 -l d -t 1 -E -x 99 >> clog +errlog=`grep_err_log` +clog_res=`cat clog | grep "|send_state:3|recv_state:3|stream_id:0|stream_type:0|send_bytes:0|read_bytes:0|recv_bytes:0|stream_len:0|"` +slog_res=`cat slog | grep "|send_state:3|recv_state:3|stream_id:0|stream_type:0|send_bytes:0|read_bytes:0|recv_bytes:0|stream_len:0|"` +if [ -z "$errlog" ] && [ -n "$clog_res" ] && [ -n "$slog_res" ] ; then + echo ">>>>>>>> pass:1" + case_print_result "h3_stream_send_pure_fin" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_stream_send_pure_fin" "fail" +fi + +rm -f test_session + +clear_log +echo -e "h3_ext_bytestream send pure fin ...\c" +./test_client -s 1024 -l d -t 1 -E -x 310 -T 2 >> clog +errlog=`grep_err_log` +clog_res=`cat clog | grep "|send_state:3|recv_state:3|stream_id:0|stream_type:0|send_bytes:5|read_bytes:2|recv_bytes:2|stream_len:2|"` +slog_res=`cat slog | grep "|send_state:3|recv_state:3|stream_id:0|stream_type:0|send_bytes:2|read_bytes:5|recv_bytes:5|stream_len:5|"` +if [ -z "$errlog" ] && [ -n "$clog_res" ] && [ -n "$slog_res" ] ; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_send_pure_fin" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_send_pure_fin" "fail" +fi + +rm -rf test_session + +killall test_server 2> /dev/null +./test_server -l d -e > /dev/null & +sleep 1 + clear_log echo -e "stream read notify fail ...\c" ./test_client -s 1024000 -l d -t 1 -E -x 12 >> clog @@ -395,7 +468,7 @@ fi clear_log echo -e "alp negotiation failure ...\c" rm -f test_session -./test_client -l e -t 1 -T -x 43 > stdlog +./test_client -l e -t 1 -T 1 -x 43 > stdlog alpn_res=`grep "xqc_ssl_alpn_select_cb|select proto error" slog` if [ -n "$alpn_res" ]; then echo ">>>>>>>> pass:1" @@ -426,7 +499,7 @@ fi clear_log rm -f test_session xqc_token tp_localhost echo -e "transport ping ...\c" -./test_client -s 1024 -l d -E -x 28 -T >> clog +./test_client -s 1024 -l d -E -x 28 -T 1 >> clog ret_ping_id=`grep "====>ping_id:" clog` ret_no_ping_id=`grep "====>no ping_id" clog` if [ -n "$ret_ping_id" ] && [ -n "$ret_no_ping_id" ]; then @@ -490,7 +563,7 @@ fi clear_log echo -e "transport only ...\c" rm -f test_session -result=`./test_client -s 1024000 -l d -T -t 1 -E|grep ">>>>>>>> pass"` +result=`./test_client -s 1024000 -l d -T 1 -t 1 -E|grep ">>>>>>>> pass"` errlog=`grep_err_log` echo "$result" if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then @@ -502,7 +575,7 @@ fi clear_log echo -e "transport 0RTT ...\c" -./test_client -s 1024000 -l e -T -t 1 -E > stdlog +./test_client -s 1024000 -l e -T 1 -t 1 -E > stdlog result=`grep ">>>>>>>> pass:" stdlog` echo "$result" flag=`grep "early_data_flag:1" stdlog` @@ -579,7 +652,7 @@ echo -e "NULL stream callback ...\c" killall test_server ./test_server -l i -e -x 2 > /dev/null & sleep 1 -./test_client -l d -T -E >> clog +./test_client -l d -T 1 -E >> clog if grep "stream_read_notify is NULL" slog >/dev/null; then echo ">>>>>>>> pass:1" case_print_result "NULL_stream_callback" "pass" @@ -812,6 +885,56 @@ else echo "$errlog" fi +clear_log +echo -e "unlimited_cc...\c" +result=`./test_client -s 102400 -l e -t 1 -E -c u|grep ">>>>>>>> pass"` +errlog=`grep_err_log` +echo "$result" +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then + case_print_result "unlimited_cc" "pass" +else + case_print_result "unlimited_cc" "fail" + echo "$errlog" +fi + +clear_log +echo -e "Copa with default parameters (delta=0.05, ai_unit=1.0) ...\c" +result=`./test_client -s 10240000 -l e -t 1 -E -c P|grep ">>>>>>>> pass"` +errlog=`grep_err_log` +echo "$result" +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then + case_print_result "copa_with_default_parameters" "pass" +else + case_print_result "copa_with_default_parameters" "fail" + echo "$errlog" +fi + +clear_log +echo -e "Copa with customized parameters (delta=0.5, ai_unit=5.0) ...\c" +result=`./test_client -s 10240000 -l e -t 1 -E -c P --copa_delta 0.5 --copa_ai_unit 5.0 |grep ">>>>>>>> pass"` +errlog=`grep_err_log` +echo "$result" +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then + case_print_result "copa_with_customized_parameters" "pass" +else + case_print_result "copa_with_customized_parameters" "fail" + echo "$errlog" +fi + + +clear_log +echo -e "low_delay_settings...\c" +result=`./test_client -s 102400 -l e -t 1 -E -x 400|grep ">>>>>>>> pass"` +errlog=`grep_err_log` +echo "$result" +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then + case_print_result "low_delay_settings" "pass" +else + case_print_result "low_delay_settings" "fail" + echo "$errlog" +fi + + clear_log result=`./test_client -s 10240000 -l e -t 1 -E -x 26|grep ">>>>>>>> pass"` errlog=`grep_err_log` @@ -934,8 +1057,8 @@ echo -e "test client long header ...\c" ./test_client -l d -x 29 >> clog #clog_res=`grep "xqc_process_conn_close_frame|with err:" clog` #slog_res=`grep "READ_VALUE error" slog` -slog_res=`grep "large nv|conn" slog` -clog_res=`grep "xqc_process_conn_close_frame|with err:" clog` +slog_res=`grep -a "large nv|conn" slog` +clog_res=`grep -a "xqc_process_conn_close_frame|with err:" clog` if [ -n "$clog_res" ] && [ -n "$slog_res" ]; then case_print_result "test_client_long_header" "pass" else @@ -1167,7 +1290,7 @@ sleep 1 clear_log echo -e "linger close transport ...\c" rm -f test_session xqc_token tp_localhost -result=`./test_client -l e -T -t 1 -E|grep ">>>>>>>> pass"` +result=`./test_client -l e -T 1 -t 1 -E|grep ">>>>>>>> pass"` errlog=`grep_err_log` echo "$result" if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then @@ -1228,7 +1351,8 @@ grep_err_log echo -e "max pkt out size...\c" -result=`./test_client -l d -x 42 -1 -E | grep ">>>>>>>> pass"` +./test_client -l d -x 42 -1 -E > stdlog +result=`grep ">>>>>>>> pass" stdlog` if [ -n "$result" ]; then echo ">>>>>>>> pass:1" case_print_result "max_pkt_out_size" "pass" @@ -1243,7 +1367,7 @@ killall test_server sleep 1 clear_log echo -e "stateless reset...\c" -./test_client -l d -x 41 -1 >> stdlog +./test_client -l d -x 41 -1 > stdlog result=`grep "receive reset, enter draining" clog` cloing_notify=`grep "conn closing: 641" stdlog` if [ -n "$result" ] && [ -n "$cloing_notify" ]; then @@ -1262,7 +1386,8 @@ sleep 1 clear_log echo -e "MPNS enable multipath negotiate ...\c" -result=`sudo ./test_client -s 1024000 -l d -t 1 -M -A -i lo | grep "enable_multipath=2"` +sudo ./test_client -s 1024000 -l d -t 1 -M -i lo > stdlog +result=` grep "enable_multipath=2" stdlog` errlog=`grep_err_log` if [ -z "$errlog" ] && [ "$result" != "" ]; then echo ">>>>>>>> pass:1" @@ -1275,7 +1400,8 @@ grep_err_log clear_log echo -e "MPNS send 1M data on multiple paths ...\c" -result=`sudo ./test_client -s 1024000 -l d -t 1 -M -A -i lo -i lo -E|grep ">>>>>>>> pass"` +sudo ./test_client -s 1024000 -l d -t 1 -M -i lo -i lo -E > stdlog +result=`grep ">>>>>>>> pass" stdlog` errlog=`grep_err_log` if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then echo ">>>>>>>> pass:1" @@ -1288,7 +1414,8 @@ grep_err_log clear_log echo -e "MPNS multipath 30 percent loss ...\c" -result=`sudo ./test_client -s 10240000 -t 5 -l e -E -d 300 -M -A -i lo -i lo|grep ">>>>>>>> pass"` +sudo ./test_client -s 10240000 -t 5 -l e -E -d 300 -M -i lo -i lo > stdlog +result=`grep ">>>>>>>> pass" stdlog` errlog=`grep_err_log` if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then echo ">>>>>>>> pass:1" @@ -1301,8 +1428,8 @@ grep_err_log clear_log echo -e "MPNS multipath close initial path ...\c" -sudo ./test_client -s 1024000 -l d -t 3 -M -A -i lo -i lo -E -x 100 >> clog -result=`grep ">>>>>>>> pass" clog` +sudo ./test_client -s 1024000 -l d -t 3 -M -i lo -i lo -E -x 100 > stdlog +result=`grep ">>>>>>>> pass" stdlog` svr_res=`grep "|path closed|path:0|" slog` cli_res=`grep "|path closed|path:0|" clog` errlog=`grep_err_log` @@ -1317,16 +1444,17 @@ grep_err_log clear_log echo -e "MPNS multipath 30 percent loss close initial path ...\c" -result=`sudo ./test_client -s 10240000 -t 5 -l d -E -d 300 -M -A -i lo -i lo -x 100|grep ">>>>>>>> pass"` +sudo ./test_client -s 10240000 -t 5 -l d -E -d 300 -M -i lo -i lo -x 100 > stdlog +result=`grep ">>>>>>>> pass" stdlog` svr_res=`grep "|path closed|path:0|" slog` cli_res=`grep "|path closed|path:0|" clog` errlog=`grep_err_log` -if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ] && [ "$svr_res" != "" ] && [ "$result" == ">>>>>>>> pass:1" ]; then +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ] && [ "$svr_res" != "" ] && [ "$cli_res" != "" ]; then echo ">>>>>>>> pass:1" - case_print_result "MPNS_multipath_10_percent_loss_close_initial_path" "pass" + case_print_result "MPNS_multipath_30_percent_loss_close_initial_path" "pass" else echo ">>>>>>>> pass:0" - case_print_result "MPNS_multipath_10_percent_loss_close_initial_path" "fail" + case_print_result "MPNS_multipath_30_percent_loss_close_initial_path" "fail" fi grep_err_log @@ -1348,11 +1476,12 @@ grep_err_log clear_log echo -e "MPNS multipath 30 percent loss close new path ...\c" -result=`sudo ./test_client -s 10240000 -t 5 -l d -E -d 300 -M -A -i lo -i lo -x 101|grep ">>>>>>>> pass"` +sudo ./test_client -s 10240000 -t 5 -l d -E -d 300 -M -i lo -i lo -x 101 > stdlog +result=`grep ">>>>>>>> pass" stdlog` svr_res=`grep "|path closed|path:1|" slog` cli_res=`grep "|path closed|path:1|" clog` errlog=`grep_err_log` -if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ] && [ "$svr_res" != "" ] && [ "$result" == ">>>>>>>> pass:1" ]; then +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ] && [ "$svr_res" != "" ] && [ "$cli_res" != "" ]; then echo ">>>>>>>> pass:1" case_print_result "MPNS_multipath_30_percent_loss_close_new_path" "pass" else @@ -1367,7 +1496,8 @@ sleep 1 clear_log echo -e "MPNS reinject unack packets by capacity ...\c" -result=`sudo ./test_client -s 1024000 -l d -t 1 -M -A -i lo -i lo -E -R 1 |grep ">>>>>>>> pass"` +sudo ./test_client -s 1024000 -l d -t 1 -M -i lo -i lo -E -R 1 > stdlog +result=`grep ">>>>>>>> pass" stdlog` errlog=`grep_err_log` if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then echo ">>>>>>>> pass:1" @@ -1385,7 +1515,8 @@ sleep 1 clear_log echo -e "MPNS reinject unack packets by deadline ...\c" -result=`sudo ./test_client -s 1024000 -l d -t 1 -M -A -i lo -i lo -E -R 2 |grep ">>>>>>>> pass"` +sudo ./test_client -s 1024000 -l d -t 1 -M -i lo -i lo -E -R 2 > stdlog +result=`grep ">>>>>>>> pass" stdlog` errlog=`grep_err_log` if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then echo ">>>>>>>> pass:1" @@ -1403,7 +1534,8 @@ sleep 1 clear_log echo -e "NAT rebinding path 0 ...\c" -result=`sudo ./test_client -s 102400 -l d -t 3 -M -A -i lo -i lo -E -n 2 -x 103 |grep ">>>>>>>> pass:0"` +sudo ./test_client -s 102400 -l d -t 3 -M -i lo -i lo -E -n 2 -x 103 > stdlog +result=`grep ">>>>>>>> pass:0" stdlog` errlog=`grep_err_log` rebind=`grep "|path:0|REBINDING|validate NAT rebinding addr|" slog` if [ -z "$errlog" ] && [ -z "$result" ] && [ "$rebind" != "" ]; then @@ -1420,7 +1552,8 @@ grep_err_log clear_log echo -e "NAT rebinding path 1 ...\c" -result=`sudo ./test_client -s 102400 -l d -t 3 -M -A -i lo -i lo -E -n 2 -x 104 |grep ">>>>>>>> pass:0"` +sudo ./test_client -s 102400 -l d -t 3 -M -i lo -i lo -E -n 2 -x 104 > stdlog +result=`grep ">>>>>>>> pass:0" stdlog` errlog=`grep_err_log` rebind=`grep "|path:1|REBINDING|validate NAT rebinding addr|" slog` if [ -z "$errlog" ] && [ -z "$result" ] && [ "$rebind" != "" ]; then @@ -1433,12 +1566,12 @@ fi grep_err_log killall test_server -./test_server -l d -e -M -Q > /dev/null & +./test_server -l d -e -M -y > /dev/null & sleep 1 clear_log echo -e "Multipath Compensate and Accelerate ...\c" -sudo ./test_client -s 102400 -l d -t 3 -M -A -i lo -i lo -E -P 2 -Q > ccfc.log +sudo ./test_client -s 102400 -l d -t 3 -M -A -i lo -i lo -E -P 2 -y > ccfc.log errlog=`grep_err_log` svr_res=`grep "path_status:2->1" slog` cli_res=`grep "path_status:2->1" clog` @@ -1453,7 +1586,7 @@ grep_err_log clear_log echo -e "Multipath Compensate but not Accelerate ...\c" -sudo ./test_client -s 102400 -l d -t 3 -M -i lo -i lo -E -P 2 -Q > ccfc.log +sudo ./test_client -s 102400 -l d -t 3 -M -i lo -i lo -E -P 2 -y > ccfc.log errlog=`grep_err_log` svr_res=`grep "path_status:2->1" slog` cli_res=`grep "path_status:2->1" clog` @@ -1467,9 +1600,2000 @@ fi grep_err_log +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi +if [ -f stdlog ]; then +. rm -f stdlog +fi +./test_server -l d -Q 9000 > /dev/null & +sleep 1 +clear_log +echo -e "datagram frame size negotiation...\c" +./test_client -l d -Q 9000 >> stdlog +cli_result=`grep "|1RTT_transport_params|max_datagram_frame_size:9000|" clog` +svr_result=`grep "|1RTT_transport_params|max_datagram_frame_size:9000|" slog` +errlog=`grep_err_log` +if [ -n "$cli_result" ] && [ -n "$svr_result" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "datagram_frame_size_negotiation" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "datagram_frame_size_negotiation" "fail" +fi + +clear_log +echo -e "0RTT max_datagram_frame_size is valid...\c" +./test_client -l d >> stdlog +cli_result=`grep "|0RTT_transport_params|max_datagram_frame_size:9000|" clog` +cli_result2=`grep "|1RTT_transport_params|max_datagram_frame_size:9000|" clog` +errlog=`grep_err_log` +if [ -n "$cli_result" ] && [ -n "$cli_result2" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "0rtt_max_datagram_frame_size_is_valid" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "0rtt_max_datagram_frame_size_is_valid" "fail" +fi + +killall test_server +./test_server -l d -Q 8000 > /dev/null & +sleep 1 +clear_log +echo -e "0RTT max_datagram_frame_size is invalid...\c" +./test_client -l d >> stdlog +cli_result=`grep "|0RTT_transport_params|max_datagram_frame_size:9000|" clog` +cli_err=`grep "[error].*err:0xe" clog` +svr_err=`grep "[error].*err:0xe" slog` +if [ -n "$cli_result" ] && [ -n "$cli_err" ] && [ -n "$svr_err" ]; then + echo ">>>>>>>> pass:1" + case_print_result "0rtt_max_datagram_frame_size_is_invalid" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "0rtt_max_datagram_frame_size_is_invalid" "fail" +fi +rm -f test_session tp_localhost xqc_token + +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi +stdbuf -oL ./test_server -l d -Q 1000 -x 200 > svr_stdlog & +sleep 1 +clear_log +echo -e "datagram_get_mss(no_saved_transport_params)...\c" +./test_client -l d -T 1 -x 200 -Q 1000 -s 1 -U 1 > stdlog +cli_res1=`grep "\[dgram-200\]|.*|initial_mss:0|" stdlog` +cli_res2=`grep "\[dgram-200\]|.*|updated_mss:997|" stdlog` +svr_res=`grep -a "\[dgram-200\]|.*|initial_mss:997|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "datagram_get_mss_no_saved_transport_params" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "datagram_get_mss_no_saved_transport_params" "fail" +fi + +> svr_stdlog +clear_log +echo -e "datagram_get_mss(saved_transport_params)...\c" +./test_client -l d -T 1 -x 200 -Q 1000 -s 1 -U 1 > stdlog +cli_res1=`grep "\[dgram-200\]|.*|initial_mss:997|" stdlog` +cli_res2=`grep "\[dgram-200\]|.*|updated_mss:997|" stdlog` +svr_res=`grep -a "\[dgram-200\]|.*|initial_mss:997|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "datagram_get_mss_saved_transport_params" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "datagram_get_mss_saved_transport_params" "fail" +fi + +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi +stdbuf -oL ./test_server -l d -Q 65535 -x 201 > svr_stdlog & +sleep 1 +clear_log +echo -e "datagram_mss_limited_by_MTU...\c" +./test_client -l d -T 1 -x 201 -Q 65535 -s 1 -U 1 > stdlog +cli_res1=`grep "\[dgram-200\]|.*|initial_mss:0|" stdlog` +cli_res2=`grep "\[dgram-200\]|.*|updated_mss:1200|" stdlog` +svr_res=`grep -a "\[dgram-200\]|.*|initial_mss:1200|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "datagram_mss_limited_by_MTU" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "datagram_mss_limited_by_MTU" "fail" +fi + +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi + +# timer-based dgram probe +stdbuf -oL ./test_server -l d -Q 65535 -x 209 -e -U 2 > svr_stdlog & +sleep 1 +clear_log +echo -e "timer_based_dgram_probe...\c" +./test_client -l d -T 1 -x 209 -s 1000 -U 1 -Q 65535 -x 209 > stdlog +killall test_server +cli_res1=`grep "|recv_dgram_bytes:3000|sent_dgram_bytes:1000|" stdlog` +svr_res=`grep -a "|recv_dgram_bytes:2000|sent_dgram_bytes:2000|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "timer_based_dgram_probe" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "timer_based_dgram_probe" "fail" +fi + +killall test_server &> /dev/null +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi + +stdbuf -oL ./test_server -l d -Q 1000 -x 200 > svr_stdlog & +sleep 1 +clear_log +echo -e "datagram_mss_limited_by_max_datagram_frame_size...\c" +./test_client -l d -T 1 -x 200 -s 1 -U 1 -Q 1000 > stdlog +cli_res1=`grep "\[dgram-200\]|.*|initial_mss:0|" stdlog` +cli_res2=`grep "\[dgram-200\]|.*|updated_mss:997|" stdlog` +svr_res=`grep -a "\[dgram-200\]|.*|initial_mss:997|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "datagram_mss_limited_by_max_datagram_frame_size" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "datagram_mss_limited_by_max_datagram_frame_size" "fail" +fi +rm -f test_session tp_localhost xqc_token + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 +clear_log +#generate 0rtt data +./test_client -l e -T 1 -s 1 -U 1 -Q 65535 > stdlog +clear_log +echo -e "send_0RTT_datagram_100KB...\c" +./test_client -l e -T 1 -s 102400 -U 1 -Q 65535 -E > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_100KB" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_100KB" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_0RTT_datagram_1MB...\c" + ./test_client -l e -T 1 -s 1048576 -U 1 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_1MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_1MB" "fail" + fi + + clear_log + echo -e "send_0RTT_datagram_10MB...\c" + ./test_client -l e -T 1 -s 10485760 -U 1 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_10MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_10MB" "fail" + fi + + clear_log + echo -e "send_0RTT_datagram_100MB...\c" + ./test_client -l e -T 1 -s 104857600 -U 1 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_100MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_100MB" "fail" + fi + +fi +rm -f test_session tp_localhost xqc_token + + +killall test_server + +./test_server -l e -Q 65535 -e -U 2 > /dev/null & +sleep 1 +clear_log +#generate 0rtt data +./test_client -l e -T 1 -s 1 -U 2 -Q 65535 > stdlog +clear_log +echo -e "send_0RTT_datagram_100KB_batch...\c" +./test_client -l e -T 1 -s 102400 -U 2 -Q 65535 -E > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_100KB_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_100KB_batch" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_0RTT_datagram_1MB_batch...\c" + ./test_client -l e -T 1 -s 1048576 -U 2 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_1MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_1MB_batch" "fail" + fi + + clear_log + echo -e "send_0RTT_datagram_10MB_batch...\c" + ./test_client -l e -T 1 -s 10485760 -U 2 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_10MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_10MB_batch" "fail" + fi + + clear_log + echo -e "send_0RTT_datagram_100MB_batch...\c" + ./test_client -l e -T 1 -s 104857600 -U 2 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_datagram_100MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_datagram_100MB_batch" "fail" + fi + +fi +rm -f test_session tp_localhost xqc_token + + +killall test_server +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_1RTT_datagram_100KB...\c" +./test_client -l e -T 1 -s 102400 -U 1 -Q 65535 -E -1 > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_100KB" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_100KB" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_1RTT_datagram_1MB...\c" + ./test_client -l e -T 1 -s 1048576 -U 1 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_1MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_1MB" "fail" + fi + + clear_log + echo -e "send_1RTT_datagram_10MB...\c" + ./test_client -l e -T 1 -s 10485760 -U 1 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_10MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_10MB" "fail" + fi + + clear_log + echo -e "send_1RTT_datagram_100MB...\c" + ./test_client -l e -T 1 -s 104857600 -U 1 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_100MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_100MB" "fail" + fi +fi +rm -f test_session tp_localhost xqc_token + + +killall test_server +./test_server -l e -Q 65535 -e -U 2 > /dev/null & +sleep 1 +clear_log +echo -e "send_1RTT_datagram_100KB_batch...\c" +./test_client -l e -T 1 -s 102400 -U 2 -Q 65535 -E -1 > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_100KB_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_100KB_batch" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_1RTT_datagram_1MB_batch...\c" + ./test_client -l e -T 1 -s 1048576 -U 2 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_1MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_1MB_batch" "fail" + fi + + clear_log + echo -e "send_1RTT_datagram_10MB_batch...\c" + ./test_client -l e -T 1 -s 10485760 -U 2 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_10MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_10MB_batch" "fail" + fi + + clear_log + echo -e "send_1RTT_datagram_100MB_batch...\c" + ./test_client -l e -T 1 -s 104857600 -U 2 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_datagram_100MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_datagram_100MB_batch" "fail" + fi +fi +rm -f test_session tp_localhost xqc_token killall test_server +./test_server -l d -Q 65535 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_queue_full...\c" +./test_client -l d -T 1 -s 40000000 -U 1 -Q 65535 -1 > stdlog +cli_res1=`grep "\[dgram\]|retry_datagram_send_later|" stdlog` +cli_res2=`grep "|too many packets used|ctl_packets_used:" clog` +cli_res3=`grep "\[dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_queue_full" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_queue_full" "fail" +fi +clear_log +echo -e "send_queue_full_batch...\c" +./test_client -l d -T 1 -s 40000000 -U 2 -Q 65535 -1 > stdlog +cli_res1=`grep "\[dgram\]|retry_datagram_send_multiple_later|" stdlog` +cli_res2=`grep "|too many packets used|ctl_packets_used:" clog` +cli_res3=`grep "\[dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_queue_full_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_queue_full_batch" "fail" +fi +clear_log +echo -e "send_0rtt_datagram_without_saved_datagram_tp...\c" +./test_client -l d -T 1 -s 999 -U 1 -Q 65535 -1 -E -x 202 > stdlog +cli_res1=`grep "\[dgram\]|retry_datagram_send_later|" stdlog` +cli_res2=`grep "|waiting_for_max_datagram_frame_size_from_peer|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_datagram_without_saved_datagram_tp" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_datagram_without_saved_datagram_tp" "fail" +fi + +clear_log +echo -e "send_0rtt_datagram_without_saved_datagram_tp_batch...\c" +./test_client -l d -T 1 -s 999 -U 2 -Q 65535 -1 -E -x 202 > stdlog +cli_res1=`grep "\[dgram\]|retry_datagram_send_multiple_later|" stdlog` +cli_res2=`grep "|waiting_for_max_datagram_frame_size_from_peer|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_datagram_without_saved_datagram_tp_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_datagram_without_saved_datagram_tp_batch" "fail" +fi + + +clear_log +echo -e "send_too_many_0rtt_datagrams...\c" +./test_client -l d -T 1 -s 40000 -U 1 -Q 65535 -E > stdlog +cli_res1=`grep "\[dgram\]|retry_datagram_send_later|" stdlog` +cli_res2=`grep "|too many 0rtt packets|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_too_many_0rtt_datagrams" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_too_many_0rtt_datagrams" "fail" +fi + +clear_log +echo -e "send_too_many_0rtt_datagrams_batch...\c" +./test_client -l d -T 1 -s 40000 -U 2 -Q 65535 -E > stdlog +cli_res1=`grep "\[dgram\]|retry_datagram_send_multiple_later|" stdlog` +cli_res2=`grep "|too many 0rtt packets|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_too_many_0rtt_datagrams_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_too_many_0rtt_datagrams_batch" "fail" +fi + +killall test_server +./test_server -l d -Q 65535 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_0rtt_datagram_reject...\c" +./test_client -l d -T 1 -s 4800 -U 1 -Q 65535 -E > stdlog +cli_res1=`grep "xqc_conn_early_data_reject" clog` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_datagram_reject" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_datagram_reject" "fail" +fi + + +killall test_server +./test_server -l d -Q 1000 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_oversized_datagram...\c" +./test_client -l d -T 1 -s 4800 -U 1 -Q 65535 -E -1 -x 203 > stdlog +cli_res1=`grep "datagram_is_too_large" clog` +cli_res2=`grep "trying_to_send_an_oversized_datagram" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_oversized_datagram" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_oversized_datagram" "fail" +fi + +clear_log +echo -e "send_oversized_datagram_batch...\c" +./test_client -l d -T 1 -s 4800 -U 2 -Q 65535 -E -1 -x 203 > stdlog +cli_res1=`grep "datagram_is_too_large" clog` +cli_res2=`grep "trying_to_send_an_oversized_datagram" stdlog` +cli_res3=`grep "|partially_sent_pkts_in_a_batch|cnt:1|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_oversized_datagram_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_oversized_datagram_batch" "fail" +fi +rm -rf tp_localhost test_session xqc_token + +killall test_server +./test_server -l d -Q 0 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_datagram_while_peer_does_not_support...\c" +./test_client -l d -T 1 -s 4800 -U 1 -Q 65535 -E -1 -x 204 > stdlog +cli_res1=`grep "|does not support datagram|" clog` +cli_res2=`grep "\[dgram\]|send_datagram_error|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_datagram_while_peer_does_not_support" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_datagram_while_peer_does_not_support" "fail" +fi + +clear_log +echo -e "send_datagram_batch_while_peer_does_not_support...\c" +./test_client -l d -T 1 -s 4800 -U 2 -Q 65535 -E -1 -x 204 > stdlog +cli_res1=`grep "|does not support datagram|" clog` +cli_res2=`grep "\[dgram\]|send_datagram_multiple_error|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_datagram_batch_while_peer_does_not_support" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_datagram_batch_while_peer_does_not_support" "fail" +fi + +killall test_server +./test_server -l d -Q 65535 -e -U 1 -s 1 > /dev/null & +sleep 1 +./test_client -l d -T 1 -s 1 -U 1 -Q 65535 -E -N > stdlog +clear_log +echo -e "send_0rtt_datagram_dgram1_lost...\c" +./test_client -l d -T 1 -s 4800 -U 1 -Q 65535 -E -x 205 -N > stdlog +cli_res1=`grep "\[dgram\]|dgram_lost|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_datagram_dgram1_lost" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_datagram_dgram1_lost" "fail" +fi + +clear_log +echo -e "send_1rtt_datagram_dgram1_lost...\c" +./test_client -l d -T 1 -s 4800 -U 1 -Q 65535 -E -x 205 -N -1 > stdlog +cli_res1=`grep "\[dgram\]|dgram_lost|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1rtt_datagram_dgram1_lost" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1rtt_datagram_dgram1_lost" "fail" +fi + +clear_log +echo -e "send_0rtt_datagram_reorder...\c" +./test_client -l d -T 1 -s 1800 -U 1 -Q 65535 -E -x 206 -N > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_datagram_reorder" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_datagram_reorder" "fail" +fi + +clear_log +echo -e "send_1rtt_datagram_reorder...\c" +./test_client -l d -T 1 -s 1800 -U 1 -Q 65535 -E -x 206 -N -1 > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1rtt_datagram_reorder" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1rtt_datagram_reorder" "fail" +fi + +clear_log +echo -e "datagram_lost_callback...\c" +./test_client -l d -T 1 -s 1000 -U 1 -Q 65535 -E -x 205 -N -1 > stdlog +cli_res1=`grep "\[dgram\]|dgram_lost|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "datagram_lost_callback" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "datagram_lost_callback" "fail" +fi + +clear_log +echo -e "datagram_acked_callback...\c" +./test_client -l d -T 1 -s 1000 -U 1 -Q 65535 -E -x 207 > stdlog +cli_res1=`grep "\[dgram\]|dgram_acked|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "datagram_acked_callback" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "datagram_acked_callback" "fail" +fi + +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi +stdbuf -oL ./test_server -l d -Q 65535 -x 208 -e -U 1 > svr_stdlog & +sleep 1 + +clear_log +echo -e "1RTT_datagram_send_redundancy...\c" +./test_client -l d -T 1 -s 2000 -U 1 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "1RTT_datagram_send_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "1RTT_datagram_send_redundancy" "fail" +fi + +if [ -f test_session ]; then + rm -f test_session +fi + +clear_log +echo -e "1RTT_datagram_send_multiple_redundancy...\c" +./test_client -l d -T 1 -s 2000 -U 2 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "1RTT_datagram_send_multiple_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "1RTT_datagram_send_multiple_redundancy" "fail" +fi + + +clear_log +echo -e "0RTT_datagram_send_redundancy...\c" +./test_client -l d -T 1 -s 2000 -U 1 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "0RTT_datagram_send_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "0RTT_datagram_send_redundancy" "fail" +fi + +clear_log +echo -e "0RTT_datagram_send_multiple_redundancy...\c" +./test_client -l d -T 1 -s 2000 -U 2 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "0RTT_datagram_send_multiple_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "0RTT_datagram_send_multiple_redundancy" "fail" +fi + + +# h3 ext datagram + +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi +stdbuf -oL ./test_server -l d -Q 1000 -x 200 > svr_stdlog & +sleep 1 +clear_log +echo -e "h3_ext_datagram_get_mss(no_saved_transport_params)...\c" +./test_client -l d -T 2 -x 200 -Q 1000 -s 1 -U 1 > stdlog +cli_res1=`grep "\[h3-dgram-200\]|.*|initial_mss:0|" stdlog` +cli_res2=`grep "\[h3-dgram-200\]|.*|updated_mss:997|" stdlog` +svr_res=`grep -a "\[h3-dgram-200\]|.*|initial_mss:997|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_datagram_get_mss_no_saved_transport_params" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_datagram_get_mss_no_saved_transport_params" "fail" +fi + +> svr_stdlog +clear_log +echo -e "h3_ext_datagram_get_mss(saved_transport_params)...\c" +./test_client -l d -T 2 -x 200 -Q 1000 -s 1 -U 1 > stdlog +cli_res1=`grep "\[h3-dgram-200\]|.*|initial_mss:997|" stdlog` +cli_res2=`grep "\[h3-dgram-200\]|.*|updated_mss:997|" stdlog` +svr_res=`grep -a "\[h3-dgram-200\]|.*|initial_mss:997|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_datagram_get_mss_saved_transport_params" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_datagram_get_mss_saved_transport_params" "fail" +fi + +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi +stdbuf -oL ./test_server -l d -Q 65535 -x 201 > svr_stdlog & +sleep 1 +clear_log +echo -e "h3_ext_datagram_mss_limited_by_MTU...\c" +./test_client -l d -T 2 -x 201 -Q 65535 -s 1 -U 1 > stdlog +cli_res1=`grep "\[h3-dgram-200\]|.*|initial_mss:0|" stdlog` +cli_res2=`grep "\[h3-dgram-200\]|.*|updated_mss:1200|" stdlog` +svr_res=`grep -a "\[h3-dgram-200\]|.*|initial_mss:1200|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_datagram_mss_limited_by_MTU" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_datagram_mss_limited_by_MTU" "fail" +fi + +killall test_server +if [ -f test_session ]; then + rm -f test_session +fi +if [ -f tp_localhost ]; then + rm -f tp_localhost +fi +if [ -f xqc_token ]; then + rm -f xqc_token +fi +stdbuf -oL ./test_server -l d -Q 1000 -x 200 > svr_stdlog & +sleep 1 +clear_log +echo -e "h3_ext_datagram_mss_limited_by_max_datagram_frame_size...\c" +./test_client -l d -T 2 -x 200 -s 1 -U 1 -Q 1000 > stdlog +cli_res1=`grep "\[h3-dgram-200\]|.*|initial_mss:0|" stdlog` +cli_res2=`grep "\[h3-dgram-200\]|.*|updated_mss:997|" stdlog` +svr_res=`grep -a "\[h3-dgram-200\]|.*|initial_mss:997|" svr_stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$svr_res" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_datagram_mss_limited_by_max_datagram_frame_size" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_datagram_mss_limited_by_max_datagram_frame_size" "fail" +fi +rm -f test_session tp_localhost xqc_token + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 +clear_log +#generate 0rtt data +./test_client -l e -T 2 -s 1 -U 1 -Q 65535 > stdlog +clear_log +echo -e "send_0RTT_h3_ext_datagram_100KB...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_100KB" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_100KB" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_0RTT_h3_ext_datagram_1MB...\c" + ./test_client -l e -T 2 -s 1048576 -U 1 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_1MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_1MB" "fail" + fi + + clear_log + echo -e "send_0RTT_h3_ext_datagram_10MB...\c" + ./test_client -l e -T 2 -s 10485760 -U 1 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_10MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_10MB" "fail" + fi + + clear_log + echo -e "send_0RTT_h3_ext_datagram_100MB...\c" + ./test_client -l e -T 2 -s 104857600 -U 1 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_100MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_100MB" "fail" + fi + +fi +rm -f test_session tp_localhost xqc_token + + +killall test_server + +./test_server -l e -Q 65535 -e -U 2 > /dev/null & +sleep 1 +clear_log +#generate 0rtt data +./test_client -l e -T 2 -s 1 -U 2 -Q 65535 > stdlog +clear_log +echo -e "send_0RTT_h3_ext_datagram_100KB_batch...\c" +./test_client -l e -T 2 -s 102400 -U 2 -Q 65535 -E > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_100KB_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_100KB_batch" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_0RTT_h3_ext_datagram_1MB_batch...\c" + ./test_client -l e -T 2 -s 1048576 -U 2 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_1MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_1MB_batch" "fail" + fi + + clear_log + echo -e "send_0RTT_h3_ext_datagram_10MB_batch...\c" + ./test_client -l e -T 2 -s 10485760 -U 2 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_10MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_10MB_batch" "fail" + fi + + clear_log + echo -e "send_0RTT_h3_ext_datagram_100MB_batch...\c" + ./test_client -l e -T 2 -s 104857600 -U 2 -Q 65535 -E > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0RTT_h3_ext_datagram_100MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_0RTT_h3_ext_datagram_100MB_batch" "fail" + fi + +fi +rm -f test_session tp_localhost xqc_token + + +killall test_server +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_1RTT_h3_ext_datagram_100KB...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -1 > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_100KB" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_100KB" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_1RTT_h3_ext_datagram_1MB...\c" + ./test_client -l e -T 2 -s 1048576 -U 1 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_1MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_1MB" "fail" + fi + + clear_log + echo -e "send_1RTT_h3_ext_datagram_10MB...\c" + ./test_client -l e -T 2 -s 10485760 -U 1 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_10MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_10MB" "fail" + fi + + clear_log + echo -e "send_1RTT_h3_ext_datagram_100MB...\c" + ./test_client -l e -T 2 -s 104857600 -U 1 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_100MB" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_100MB" "fail" + fi +fi +rm -f test_session tp_localhost xqc_token + + +killall test_server +./test_server -l e -Q 65535 -e -U 2 > /dev/null & +sleep 1 +clear_log +echo -e "send_1RTT_h3_ext_datagram_100KB_batch...\c" +./test_client -l e -T 2 -s 102400 -U 2 -Q 65535 -E -1 > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_100KB_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_100KB_batch" "fail" +fi + +if [ $LOCAL_TEST -ne 0 ]; then + clear_log + echo -e "send_1RTT_h3_ext_datagram_1MB_batch...\c" + ./test_client -l e -T 2 -s 1048576 -U 2 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_1MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_1MB_batch" "fail" + fi + + clear_log + echo -e "send_1RTT_h3_ext_datagram_10MB_batch...\c" + ./test_client -l e -T 2 -s 10485760 -U 2 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_10MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_10MB_batch" "fail" + fi + + clear_log + echo -e "send_1RTT_h3_ext_datagram_100MB_batch...\c" + ./test_client -l e -T 2 -s 104857600 -U 2 -Q 65535 -E -1 > stdlog + cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` + errlog=`grep_err_log` + if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1RTT_h3_ext_datagram_100MB_batch" "pass" + else + echo ">>>>>>>> pass:0" + case_print_result "send_1RTT_h3_ext_datagram_100MB_batch" "fail" + fi +fi +rm -f test_session tp_localhost xqc_token + +killall test_server +./test_server -l d -Q 65535 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "h3_ext_dgram_send_queue_full...\c" +./test_client -l d -T 2 -s 40000000 -U 1 -Q 65535 -1 > stdlog +cli_res1=`grep "\[h3-dgram\]|retry_datagram_send_later|" stdlog` +cli_res2=`grep "|too many packets used|ctl_packets_used:" clog` +cli_res3=`grep "\[h3-dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_dgram_send_queue_full" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_dgram_send_queue_full" "fail" +fi + +clear_log +echo -e "h3_ext_dgram_send_queue_full_batch...\c" +./test_client -l d -T 2 -s 40000000 -U 2 -Q 65535 -1 > stdlog +cli_res1=`grep "\[h3-dgram\]|retry_datagram_send_multiple_later|" stdlog` +cli_res2=`grep "|too many packets used|ctl_packets_used:" clog` +cli_res3=`grep "\[h3-dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_dgram_send_queue_full_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_dgram_send_queue_full_batch" "fail" +fi + +clear_log +echo -e "send_0rtt_h3_ext_datagram_without_saved_datagram_tp...\c" +./test_client -l d -T 2 -s 999 -U 1 -Q 65535 -1 -E -x 202 > stdlog +cli_res1=`grep "\[h3-dgram\]|retry_datagram_send_later|" stdlog` +cli_res2=`grep "|waiting_for_max_datagram_frame_size_from_peer|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[h3-dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_h3_ext_datagram_without_saved_datagram_tp" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_h3_ext_datagram_without_saved_datagram_tp" "fail" +fi + +clear_log +echo -e "send_0rtt_h3_ext_datagram_without_saved_datagram_tp_batch...\c" +./test_client -l d -T 2 -s 999 -U 2 -Q 65535 -1 -E -x 202 > stdlog +cli_res1=`grep "\[h3-dgram\]|retry_datagram_send_multiple_later|" stdlog` +cli_res2=`grep "|waiting_for_max_datagram_frame_size_from_peer|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[h3-dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_h3_ext_datagram_without_saved_datagram_tp_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_h3_ext_datagram_without_saved_datagram_tp_batch" "fail" +fi + + +clear_log +echo -e "send_too_many_0rtt_h3_ext_datagrams...\c" +./test_client -l d -T 2 -s 40000 -U 1 -Q 65535 -E > stdlog +cli_res1=`grep "\[h3-dgram\]|retry_datagram_send_later|" stdlog` +cli_res2=`grep "|too many 0rtt packets|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[h3-dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_too_many_0rtt_h3_ext_datagrams" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_too_many_0rtt_h3_ext_datagrams" "fail" +fi + +clear_log +echo -e "send_too_many_0rtt_h3_ext_datagrams_batch...\c" +./test_client -l d -T 2 -s 40000 -U 2 -Q 65535 -E > stdlog +cli_res1=`grep "\[h3-dgram\]|retry_datagram_send_multiple_later|" stdlog` +cli_res2=`grep "|too many 0rtt packets|" clog` +cli_res3=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res4=`grep "\[h3-dgram\]|dgram_write|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_too_many_0rtt_h3_ext_datagrams_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_too_many_0rtt_h3_ext_datagrams_batch" "fail" +fi + +killall test_server +./test_server -l d -Q 65535 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_0rtt_h3_ext_datagram_reject...\c" +./test_client -l d -T 2 -s 4800 -U 1 -Q 65535 -E > stdlog +cli_res1=`grep "xqc_conn_early_data_reject" clog` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_h3_ext_datagram_reject" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_h3_ext_datagram_reject" "fail" +fi + + +killall test_server +./test_server -l d -Q 1000 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_oversized_h3_ext_datagram...\c" +./test_client -l d -T 2 -s 4800 -U 1 -Q 65535 -E -1 -x 203 > stdlog +cli_res1=`grep "datagram_is_too_large" clog` +cli_res2=`grep "trying_to_send_an_oversized_datagram" stdlog` +#errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_oversized_h3_ext_datagram" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_oversized_h3_ext_datagram" "fail" +fi + +clear_log +echo -e "send_oversized_h3_ext_datagram_batch...\c" +./test_client -l d -T 2 -s 4800 -U 2 -Q 65535 -E -1 -x 203 > stdlog +cli_res1=`grep "datagram_is_too_large" clog` +cli_res2=`grep "trying_to_send_an_oversized_datagram" stdlog` +cli_res3=`grep "|partially_sent_pkts_in_a_batch|cnt:1|" stdlog` +#errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_oversized_h3_ext_datagram_batch" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_oversized_h3_ext_datagram_batch" "fail" +fi +rm -rf tp_localhost test_session xqc_token + +killall test_server +./test_server -l d -Q 0 -e -U 1 -s 1 > /dev/null & +sleep 1 +clear_log +echo -e "send_h3_ext_datagram_while_peer_does_not_support...\c" +./test_client -l d -T 2 -s 4800 -U 1 -Q 65535 -E -1 -x 204 > stdlog +cli_res1=`grep "|does not support datagram|" clog` +cli_res2=`grep "\[h3-dgram\]|send_datagram_error|" stdlog` +#errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_h3_ext_datagram_while_peer_does_not_support" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_h3_ext_datagram_while_peer_does_not_support" "fail" +fi + +clear_log +echo -e "send_h3_ext_datagram_batch_while_peer_does_not_support...\c" +./test_client -l d -T 2 -s 4800 -U 2 -Q 65535 -E -1 -x 204 > stdlog +cli_res1=`grep "|does not support datagram|" clog` +cli_res2=`grep "\[h3-dgram\]|send_datagram_multiple_error|" stdlog` +#errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_h3_ext_datagram_batch_while_peer_does_not_support" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_h3_ext_datagram_batch_while_peer_does_not_support" "fail" +fi + +killall test_server +./test_server -l d -Q 65535 -e -U 1 -s 1 > /dev/null & +sleep 1 +./test_client -l d -T 2 -s 1 -U 1 -Q 65535 -E -N > stdlog +clear_log +echo -e "send_0rtt_h3_ext_datagram_dgram1_lost...\c" +./test_client -l d -T 2 -s 4800 -U 1 -Q 65535 -E -x 205 -N > stdlog +cli_res1=`grep "\[h3-dgram\]|dgram_lost|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_h3_ext_datagram_dgram1_lost" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_h3_ext_datagram_dgram1_lost" "fail" +fi + +clear_log +echo -e "send_1rtt_h3_ext_datagram_dgram1_lost...\c" +./test_client -l d -T 2 -s 4800 -U 1 -Q 65535 -E -x 205 -N -1 > stdlog +cli_res1=`grep "\[h3-dgram\]|dgram_lost|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1rtt_h3_ext_datagram_dgram1_lost" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1rtt_h3_ext_datagram_dgram1_lost" "fail" +fi + +clear_log +echo -e "send_0rtt_h3_ext_datagram_reorder...\c" +./test_client -l d -T 2 -s 1800 -U 1 -Q 65535 -E -x 206 -N > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_0rtt_h3_ext_datagram_reorder" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_0rtt_h3_ext_datagram_reorder" "fail" +fi + +clear_log +echo -e "send_1rtt_h3_ext_datagram_reorder...\c" +./test_client -l d -T 2 -s 1800 -U 1 -Q 65535 -E -x 206 -N -1 > stdlog +cli_res1=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "send_1rtt_h3_ext_datagram_reorder" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "send_1rtt_h3_ext_datagram_reorder" "fail" +fi + +clear_log +echo -e "h3_ext_datagram_lost_callback...\c" +./test_client -l d -T 2 -s 1000 -U 1 -Q 65535 -E -x 205 -N -1 > stdlog +cli_res1=`grep "\[h3-dgram\]|dgram_lost|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_datagram_lost_callback" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_datagram_lost_callback" "fail" +fi + +clear_log +echo -e "h3_ext_datagram_acked_callback...\c" +./test_client -l d -T 2 -s 1000 -U 1 -Q 65535 -E -x 207 > stdlog +cli_res1=`grep "\[h3-dgram\]|dgram_acked|dgram_id:0|" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_datagram_acked_callback" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_datagram_acked_callback" "fail" +fi + +rm -f test_session tp_localhost xqc_token + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 -x 208 > /dev/null & +sleep 1 + +clear_log +echo -e "1RTT_h3_ext_datagram_send_redundancy...\c" +./test_client -l d -T 2 -s 2000 -U 1 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[h3-dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "1RTT_h3_ext_datagram_send_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "1RTT_h3_ext_datagram_send_redundancy" "fail" +fi + +rm -f test_session tp_localhost xqc_token + +clear_log +echo -e "1RTT_h3_ext_datagram_send_multiple_redundancy...\c" +./test_client -l d -T 2 -s 2000 -U 2 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[h3-dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "1RTT_h3_ext_datagram_send_multiple_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "1RTT_h3_ext_datagram_send_multiple_redundancy" "fail" +fi + +clear_log +echo -e "0RTT_h3_ext_datagram_send_redundancy...\c" +./test_client -l d -T 2 -s 2000 -U 1 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[h3-dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "0RTT_h3_ext_datagram_send_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "0RTT_h3_ext_datagram_send_redundancy" "fail" +fi + +clear_log +echo -e "0RTT_h3_ext_datagram_send_multiple_redundancy...\c" +./test_client -l d -T 2 -s 2000 -U 2 -Q 65535 -x 208 > stdlog +cli_res1=`grep "\[h3-dgram\]|recv_dgram_bytes:8000" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "0RTT_h3_ext_datagram_send_multiple_redundancy" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "0RTT_h3_ext_datagram_send_multiple_redundancy" "fail" +fi + + +# send h3 request / bytestream / datagram in one h3_conn (-x 300) + +rm -f test_session tp_localhost xqc_token + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 + + +## 1RTT +clear_log +echo -e "h3_ext_1RTT_send_test...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 300 -1 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|bytes_rcvd:102400|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog` +cli_res6=`grep "early_data_flag:0" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -n "$cli_res5" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_1RTT_send_test" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_1RTT_send_test" "fail" +fi + +## 0RTT +clear_log +echo -e "h3_ext_0RTT_accept_send_test...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 300 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|bytes_rcvd:102400|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog` +cli_res6=`grep "early_data_flag:1" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -n "$cli_res5" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_accept_send_test" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_accept_send_test" "fail" +fi + +## 0RTT reject + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 + +clear_log +echo -e "h3_ext_0RTT_reject_send_test...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 300 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|bytes_rcvd:102400|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog` +cli_res6=`grep "early_data_flag:2" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ -n "$cli_res5" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_reject_send_test" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_reject_send_test" "fail" +fi + +# send concurrent h3 req / open concurrent bytestreams / send datagrams in one h3_conn (-x 301) + +## 1RTT +clear_log +echo -e "h3_ext_1RTT_concurrent_send_test...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 301 -P 2 -1 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|bytes_rcvd:102400|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:0" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "2" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "2" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_1RTT_concurrent_send_test" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_1RTT_concurrent_send_test" "fail" +fi + +## 0RTT +clear_log +echo -e "h3_ext_0RTT_accept_concurrent_send_test...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 301 -P 2 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|bytes_rcvd:102400|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:1" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "2" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "2" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_accept_concurrent_send_test" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_accept_concurrent_send_test" "fail" +fi + +## 0RTT reject + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 + +clear_log +echo -e "h3_ext_0RTT_reject_concurrent_send_test...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 301 -P 2 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|bytes_rcvd:102400|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:2" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "2" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "2" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_reject_concurrent_send_test" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_reject_concurrent_send_test" "fail" +fi + + +# send bytestream with pure fin (-x 302 -x 303) + +## 1RTT + +clear_log +echo -e "h3_ext_1RTT_send_pure_fin1...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 302 -1 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:0|bytes_rcvd:0|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:no|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:0" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_1RTT_send_pure_fin1" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_1RTT_send_pure_fin1" "fail" +fi + +clear_log +echo -e "h3_ext_1RTT_send_pure_fin2...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 303 -1 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:0|bytes_rcvd:0|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:no|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:0" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_1RTT_send_pure_fin2" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_1RTT_send_pure_fin2" "fail" +fi + +## 0RTT + +clear_log +echo -e "h3_ext_0RTT_accept_send_pure_fin1...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 302 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:0|bytes_rcvd:0|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:no|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:1" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_accept_send_pure_fin1" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_accept_send_pure_fin1" "fail" +fi + +clear_log +echo -e "h3_ext_0RTT_accept_send_pure_fin2...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 303 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:0|bytes_rcvd:0|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:no|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:1" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_accept_send_pure_fin2" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_accept_send_pure_fin2" "fail" +fi + +## 0RTT reject + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 + +clear_log +echo -e "h3_ext_0RTT_reject_send_pure_fin1...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 302 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:0|bytes_rcvd:0|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:no|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:2" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_reject_send_pure_fin1" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_reject_send_pure_fin1" "fail" +fi + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 + +clear_log +echo -e "h3_ext_0RTT_reject_send_pure_fin2...\c" +./test_client -l e -T 2 -s 102400 -U 1 -Q 65535 -E -x 303 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:0|bytes_rcvd:0|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:no|" stdlog | wc -l` +cli_res6=`grep "early_data_flag:2" stdlog` + +errlog=`grep_err_log` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_0RTT_reject_send_pure_fin2" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_0RTT_reject_send_pure_fin2" "fail" +fi + +# finish bytestream during transmission (-x 304) + +clear_log +echo -e "h3_ext_finish_bytestream_during_transmission...\c" +./test_client -l d -T 2 -s 102400 -U 1 -Q 65535 -E -x 304 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|bytes_rcvd:102400|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +cli_res6=`grep "send pure fin" clog` +errlog=`grep_err_log` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_finish_bytestream_during_transmission" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_finish_bytestream_during_transmission" "fail" +fi + +# close bytestream during transmission (-x 305) + +clear_log +echo -e "h3_ext_close_bytestream_during_transmission...\c" +./test_client -l d -T 2 -s 102400 -U 1 -Q 65535 -E -x 305 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:yes|" stdlog` +cli_res3=`grep "\[h3-dgram\]|recv_dgram_bytes:102400|sent_dgram_bytes:102400|lost_dgram_bytes:0|lost_cnt:0|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:102400|.*|recv_fin:0|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:.*|" stdlog | wc -l` +cli_res6=`grep "xqc_h3_ext_bytestream_close|success" clog` +errlog=`grep_err_log | grep -v "xqc_h3_stream_process_data|xqc_stream_recv"` +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -n "$cli_res6" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_close_bytestream_during_transmission" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_close_bytestream_during_transmission" "fail" +fi + +# bytestream write blocked by streamlevel flowctl (-x 306) + +clear_log +echo -e "h3_ext_bytestream_blocked_by_stream_flowctl...\c" +./test_client -l d -T 2 -s 32000000 -U 1 -Q 65535 -E -x 306 > stdlog +cli_res2=`grep "|xqc_stream_send|exceed max_stream_data" clog` +cli_res3=`grep "|h3_ext_bytestream_write_notify|success|" clog` +cli_res4=`grep "\[bytestream\]|bytes_sent:32000000|bytes_rcvd:32000000|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_blocked_by_stream_flowctl" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_blocked_by_stream_flowctl" "fail" +fi + +# bytestream write blocked by 0RTT limit (-x 307) + +clear_log +echo -e "h3_ext_bytestream_blocked_by_0RTT_limit...\c" +./test_client -l d -T 2 -s 10000000 -U 1 -Q 65535 -E -x 307 > stdlog +cli_res2=`grep "|too many 0rtt packets|" clog` +cli_res3=`grep "|h3_ext_bytestream_write_notify|success|" clog` +cli_res4=`grep "\[bytestream\]|bytes_sent:10000000|bytes_rcvd:10000000|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_blocked_by_0RTT_limit" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_blocked_by_0RTT_limit" "fail" +fi + +# bytestream 0RTT write blocked by no 0RTT support (-x 308) + +clear_log +echo -e "h3_ext_bytestream_blocked_by_no_0RTT_support...\c" +./test_client -l d -T 2 -s 1024 -U 1 -Q 65535 -E -x 308 -1 > stdlog +cli_res2=`grep "|blocked by no 0RTT support|" clog` +cli_res3=`grep "|h3_ext_bytestream_write_notify|success|" clog` +cli_res4=`grep "\[bytestream\]|bytes_sent:1024|bytes_rcvd:1024|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_blocked_by_no_0RTT_support" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_blocked_by_no_0RTT_support" "fail" +fi + +# bytestream/h3_request/datagram all blocked by sndq size (-x 309) + +clear_log +echo -e "h3_ext_bytestream_blocked_by_sndq_full...\c" +./test_client -l e -T 2 -s 16000000 -U 1 -Q 65535 -E -x 309 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:.*|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:16000000|bytes_rcvd:16000000|recv_fin:1|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` + +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_blocked_by_sndq_full" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_blocked_by_sndq_full" "fail" +fi + +# read / write full messages even if blocking happens + +clear_log +echo -e "h3_ext_bytestream_full_message_flow_ctrl...\c" +./test_client -l d -T 2 -s 32000000 -U 1 -Q 65535 -E -x 311 > stdlog +cli_res2=`grep "|xqc_stream_send|exceed max_stream_data" clog` +cli_res3=`grep "|h3_ext_bytestream_write_notify|success|" clog` +cli_res4=`grep "\[bytestream\]|bytes_sent:32001000|bytes_rcvd:32001000|recv_fin:1|snd_times:2|rcv_times:2|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_full_message_under_flow_ctrl" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_full_message_under_flow_ctrl" "fail" +fi + +clear_log +echo -e "h3_ext_bytestream_full_message_0RTT_blocking...\c" +./test_client -l d -T 2 -s 10000000 -U 1 -Q 65535 -E -x 312 > stdlog +cli_res2=`grep "|too many 0rtt packets|" clog` +cli_res3=`grep "|h3_ext_bytestream_write_notify|success|" clog` +cli_res4=`grep "\[bytestream\]|bytes_sent:10001000|bytes_rcvd:10001000|recv_fin:1|snd_times:2|rcv_times:2|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_full_message_0RTT_blocking" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_full_message_0RTT_blocking" "fail" +fi + +clear_log +echo -e "h3_ext_bytestream_full_message_no_0RTT_suppport...\c" +./test_client -l d -T 2 -s 1024 -U 1 -Q 65535 -E -x 313 -1 > stdlog +cli_res2=`grep "|blocked by no 0RTT support|" clog` +cli_res3=`grep "|h3_ext_bytestream_write_notify|success|" clog` +cli_res4=`grep "\[bytestream\]|bytes_sent:2024|bytes_rcvd:2024|recv_fin:1|snd_times:2|rcv_times:2|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$cli_res3" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_full_message_no_0RTT_suppport" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_full_message_no_0RTT_suppport" "fail" +fi + +clear_log +echo -e "h3_ext_bytestream_full_message_sndq_full...\c" +./test_client -l e -T 2 -s 16000000 -U 1 -Q 65535 -E -x 314 > stdlog +cli_res1=`grep ">>>>>>>> pass:1" stdlog | wc -l` +cli_res2=`grep "\[dgram\]|echo_check|same_content:.*|" stdlog` +cli_res4=`grep "\[bytestream\]|bytes_sent:16001000|bytes_rcvd:16001000|recv_fin:1|snd_times:2|rcv_times:2|" stdlog` +cli_res5=`grep "\[bytestream\]|same_content:yes|" stdlog | wc -l` +errlog=`grep_err_log` + +if [ "$cli_res1" == "1" ] && [ -n "$cli_res2" ] && [ -n "$cli_res4" ] && [ "$cli_res5" == "1" ] && [ -z "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_bytestream_full_message_sndq_full" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_bytestream_full_message_sndq_full" "fail" +fi + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 -H > /dev/null & +sleep 1 + +clear_log +echo -e "connect to an h3_ext disabled server...\c" +./test_client -l e -T 2 -s 1024 -U 1 -Q 65535 -E > stdlog +svr_log=`grep "select proto error" slog` + +if [ -n "$svr_log" ]; then + echo ">>>>>>>> pass:1" + case_print_result "connect_to_an_h3_ext_disabled_server" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "connect_to_an_h3_ext_disabled_server" "fail" +fi + +killall test_server + +./test_server -l e -Q 65535 -e -U 1 > /dev/null & +sleep 1 + +clear_log +echo -e "h3_ext is disabled on the client...\c" +./test_client -l e -T 2 -s 1024 -U 1 -Q 65535 -E -x 315 > stdlog +cli_res1=`grep "can't get application layer callback" clog` + +if [ -n "$cli_res1" ]; then + echo ">>>>>>>> pass:1" + case_print_result "h3_ext_is_disabled_on_the_client" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "h3_ext_is_disabled_on_the_client" "fail" +fi + +rm -rf tp_localhost test_session xqc_token +killall test_server + + +./test_server -l d -Q 65535 -e -U 1 -s 1 > /dev/null & +sleep 1 +./test_client -l d -T 1 -s 4800 -U 1 -Q 65535 -E &> /dev/null #generate 0rtt ticket +killall test_server +./test_server -l d -e -s 1 > /dev/null & #disable datagram +sleep 1 +clear_log +echo -e "check_clear_0rtt_ticket_flag_in_close_notify...\c" +./test_client -l d -T 1 -s 4800 -U 1 -Q 65535 -E > stdlog +cli_res2=`grep "should_clear_0rtt_ticket, conn_err:14, clear_0rtt_ticket:1" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "check_clear_0rtt_ticket_flag_in_close_notify" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "check_clear_0rtt_ticket_flag_in_close_notify" "fail" +fi + +rm -rf tp_localhost test_session xqc_token +killall test_server + +./test_server -l d -Q 65535 -e -s 1 > /dev/null & +sleep 1 +./test_client -l d -s 4800 -Q 65535 -E &> /dev/null #generate 0rtt ticket +killall test_server +./test_server -l d -e -s 1 > /dev/null & #disable datagram +sleep 1 +clear_log +echo -e "check_clear_0rtt_ticket_flag_in_h3_close_notify...\c" +./test_client -l d -s 4800 -Q 65535 -E > stdlog +cli_res2=`grep "should_clear_0rtt_ticket, conn_err:14, clear_0rtt_ticket:1" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "check_clear_0rtt_ticket_flag_in_h3_close_notify" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "check_clear_0rtt_ticket_flag_in_h3_close_notify" "fail" +fi + +rm -rf tp_localhost test_session xqc_token +killall test_server + +./test_server -l d -Q 65535 -e -s 1 > /dev/null & +sleep 1 +./test_client -l d -s 4800 -Q 65535 -E &> /dev/null #generate 0rtt ticket +killall test_server +./test_server -l d -e -s 1 > /dev/null & #disable datagram +sleep 1 +clear_log +echo -e "check_clear_0rtt_ticket_flag_in_h3_close_notify...\c" +./test_client -l d -s 4800 -Q 65535 -E > stdlog +cli_res2=`grep "should_clear_0rtt_ticket, conn_err:14, clear_0rtt_ticket:1" stdlog` +errlog=`grep_err_log` +if [ -n "$cli_res2" ] && [ -n "$errlog" ]; then + echo ">>>>>>>> pass:1" + case_print_result "check_clear_0rtt_ticket_flag_in_h3_close_notify" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "check_clear_0rtt_ticket_flag_in_h3_close_notify" "fail" +fi + +rm -rf tp_localhost test_session xqc_token +killall test_server cd - diff --git a/scripts/xquic.lds b/scripts/xquic.lds index f2a324108..2b031158f 100644 --- a/scripts/xquic.lds +++ b/scripts/xquic.lds @@ -82,7 +82,32 @@ XQUIC_VERS_1.0 { xqc_path_get_peer_addr; xqc_path_get_local_addr; xqc_xlink_reinj_ctl_cb; + xqc_datagram_get_mss; + xqc_datagram_send; + xqc_datagram_send_multiple; + xqc_datagram_set_user_data; + xqc_datagram_get_user_data; xqc_path_get_stats; + xqc_h3_ext_datagram_get_mss; + xqc_h3_ext_datagram_set_user_data; + xqc_h3_ext_datagram_get_user_data; + xqc_h3_ext_datagram_send; + xqc_h3_ext_datagram_send_multiple; + xqc_h3_ext_bytestream_create; + xqc_h3_ext_bytestream_close; + xqc_h3_ext_bytestream_finish; + xqc_h3_ext_bytestream_set_user_data; + xqc_h3_ext_bytestream_get_user_data; + xqc_h3_ext_bytestream_get_stats; + xqc_h3_ext_bytestream_send; + xqc_h3_ext_bytestream_id; + xqc_h3_ext_bytestream_get_h3_conn; + xqc_conn_should_clear_0rtt_ticket; + xqc_unlimited_cc_cb; + xqc_copa_cb; + xqc_conn_get_conn_settings_template; + xqc_conn_get_lastest_rtt; + xqc_log_enable; local: *; }; diff --git a/src/common/xqc_log.c b/src/common/xqc_log.c index 06c185bab..0077fcb42 100644 --- a/src/common/xqc_log.c +++ b/src/common/xqc_log.c @@ -9,6 +9,14 @@ FILE *g_malloc_info_fp; #endif +static xqc_bool_t xqc_log_switch = XQC_TRUE; + +void +xqc_log_enable(xqc_bool_t enable) +{ + xqc_log_switch = enable; +} + void xqc_log_level_set(xqc_log_t *log, xqc_log_level_t level) { @@ -166,12 +174,17 @@ xqc_log_type_str(xqc_log_type_t type) void xqc_log_implement(xqc_log_t *log, xqc_log_type_t type, const char *func, const char *fmt, ...) { + /* do nothing if switch is off */ + if (!xqc_log_switch) { + return; + } + xqc_log_level_t level = xqc_log_type_2_level(type); if (level > log->log_level) { return; } - unsigned char buf[XQC_MAX_LOG_LEN]; + unsigned char buf[XQC_MAX_LOG_LEN] = {0}; unsigned char *p = buf; unsigned char *last = buf + sizeof(buf); diff --git a/src/common/xqc_log_event_callback.c b/src/common/xqc_log_event_callback.c index bb4a0d1c3..025fcb9ad 100644 --- a/src/common/xqc_log_event_callback.c +++ b/src/common/xqc_log_event_callback.c @@ -287,6 +287,13 @@ xqc_log_TRA_FRAMES_PROCESSED_callback(xqc_log_t *log, const char *func, ...) break; } + case XQC_FRAME_DATAGRAM: { + uint64_t length = va_arg(args, uint64_t); + xqc_log_implement(log, TRA_FRAMES_PROCESSED, func, + "|type:%d|data_length:%ui|", frame_type, length); + break; + } + case XQC_FRAME_MAX_DATA: { uint64_t max_data = va_arg(args, uint64_t); xqc_log_implement(log, TRA_FRAMES_PROCESSED, func, diff --git a/src/congestion_control/xqc_cubic.c b/src/congestion_control/xqc_cubic.c index 6bce7a340..d9307d2a5 100644 --- a/src/congestion_control/xqc_cubic.c +++ b/src/congestion_control/xqc_cubic.c @@ -18,6 +18,7 @@ #define XQC_CUBIC_MAX_SSTHRESH 0xFFFFFFFF #define XQC_CUBIC_MIN_WIN (4 * XQC_CUBIC_MSS) +#define XQC_CUBIC_MAX_MIN_WIN (16 * XQC_CUBIC_MSS) #define XQC_CUBIC_MAX_INIT_WIN (100 * XQC_CUBIC_MSS) #define XQC_CUBIC_INIT_WIN (32 * XQC_CUBIC_MSS) @@ -127,12 +128,18 @@ xqc_cubic_init(void *cong_ctl, xqc_send_ctl_t *ctl_ctx, xqc_cc_params_t cc_param cubic->last_max_cwnd = XQC_CUBIC_INIT_WIN; cubic->ssthresh = XQC_CUBIC_MAX_SSTHRESH; cubic->congestion_recovery_start_time = 0; + cubic->init_cwnd = XQC_CUBIC_INIT_WIN; + cubic->min_cwnd = XQC_CUBIC_MIN_WIN; if (cc_params.customize_on) { + cc_params.min_cwnd *= XQC_CUBIC_MSS; cc_params.init_cwnd *= XQC_CUBIC_MSS; cubic->init_cwnd = cc_params.init_cwnd >= XQC_CUBIC_MIN_WIN && cc_params.init_cwnd <= XQC_CUBIC_MAX_INIT_WIN ? cc_params.init_cwnd : XQC_CUBIC_INIT_WIN; + cubic->min_cwnd = + cc_params.min_cwnd >= XQC_CUBIC_MIN_WIN && cc_params.min_cwnd <= XQC_CUBIC_MAX_MIN_WIN ? + cc_params.min_cwnd : XQC_CUBIC_MIN_WIN; } } @@ -163,7 +170,7 @@ xqc_cubic_on_lost(void *cong_ctl, xqc_usec_t lost_sent_time) /* Multiplicative Decrease */ cubic->cwnd = cubic->cwnd * XQC_CUBIC_BETA / XQC_CUBIC_BETA_SCALE; - cubic->cwnd = xqc_max(cubic->cwnd, XQC_CUBIC_MIN_WIN); + cubic->cwnd = xqc_max(cubic->cwnd, cubic->min_cwnd); cubic->tcp_cwnd = cubic->cwnd; cubic->ssthresh = cubic->cwnd; } @@ -215,9 +222,9 @@ xqc_cubic_reset_cwnd(void *cong_ctl) { xqc_cubic_t *cubic = (xqc_cubic_t *)(cong_ctl); cubic->epoch_start = 0; - cubic->cwnd = XQC_CUBIC_MIN_WIN; - cubic->tcp_cwnd = XQC_CUBIC_MIN_WIN; - cubic->last_max_cwnd = XQC_CUBIC_MIN_WIN; + cubic->cwnd = cubic->min_cwnd; + cubic->tcp_cwnd = cubic->min_cwnd; + cubic->last_max_cwnd = cubic->min_cwnd; } int32_t diff --git a/src/congestion_control/xqc_cubic.h b/src/congestion_control/xqc_cubic.h index 48b44997c..8d70fa6b9 100644 --- a/src/congestion_control/xqc_cubic.h +++ b/src/congestion_control/xqc_cubic.h @@ -11,6 +11,7 @@ #include "src/transport/xqc_packet_out.h" typedef struct { + uint32_t min_cwnd; uint64_t init_cwnd; /* initial window size in MSS */ uint64_t cwnd; /* current window size in bytes */ uint64_t tcp_cwnd; /* cwnd calculated according to Reno's algorithm */ diff --git a/src/congestion_control/xqc_unlimited_cc.c b/src/congestion_control/xqc_unlimited_cc.c new file mode 100644 index 000000000..258998482 --- /dev/null +++ b/src/congestion_control/xqc_unlimited_cc.c @@ -0,0 +1,74 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#include "src/congestion_control/xqc_unlimited_cc.h" +#include "src/common/xqc_config.h" + +size_t +xqc_unlimited_cc_size() +{ + return 0; +} + +static void +xqc_unlimited_cc_init(void *cong_ctl, xqc_send_ctl_t *ctl_ctx, xqc_cc_params_t cc_params) +{ + return; +} + + +static void +xqc_unlimited_cc_on_lost(void *cong_ctl, xqc_usec_t lost_sent_time) +{ + return; +} + + +static void +xqc_unlimited_cc_on_ack(void *cong_ctl, xqc_packet_out_t *po, xqc_usec_t now) +{ + return; +} + +uint64_t +xqc_unlimited_cc_get_cwnd(void *cong_ctl) +{ + return XQC_MAX_UINT64_VALUE; +} + +void +xqc_unlimited_cc_reset_cwnd(void *cong_ctl) +{ + return; +} + +int32_t +xqc_unlimited_cc_in_slow_start(void *cong_ctl) +{ + return 0; +} + +void +xqc_unlimited_cc_restart_from_idle(void *cong_ctl, uint64_t arg) +{ + return; +} + +static int +xqc_unlimited_cc_in_recovery(void *cong_ctl) +{ + return 0; +} + +const xqc_cong_ctrl_callback_t xqc_unlimited_cc_cb = { + .xqc_cong_ctl_size = xqc_unlimited_cc_size, + .xqc_cong_ctl_init = xqc_unlimited_cc_init, + .xqc_cong_ctl_on_lost = xqc_unlimited_cc_on_lost, + .xqc_cong_ctl_on_ack = xqc_unlimited_cc_on_ack, + .xqc_cong_ctl_get_cwnd = xqc_unlimited_cc_get_cwnd, + .xqc_cong_ctl_reset_cwnd = xqc_unlimited_cc_reset_cwnd, + .xqc_cong_ctl_in_slow_start = xqc_unlimited_cc_in_slow_start, + .xqc_cong_ctl_restart_from_idle = xqc_unlimited_cc_restart_from_idle, + .xqc_cong_ctl_in_recovery = xqc_unlimited_cc_in_recovery, +}; diff --git a/src/congestion_control/xqc_unlimited_cc.h b/src/congestion_control/xqc_unlimited_cc.h new file mode 100644 index 000000000..6ba04022a --- /dev/null +++ b/src/congestion_control/xqc_unlimited_cc.h @@ -0,0 +1,13 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#ifndef _XQC_UNLIMITED_CC_H_INCLUDED_ +#define _XQC_UNLIMITED_CC_H_INCLUDED_ + +#include +#include + +extern const xqc_cong_ctrl_callback_t xqc_unlimited_cc_cb; + +#endif diff --git a/src/http3/frame/xqc_h3_frame.c b/src/http3/frame/xqc_h3_frame.c index cb56f1d14..162c7b51a 100644 --- a/src/http3/frame/xqc_h3_frame.c +++ b/src/http3/frame/xqc_h3_frame.c @@ -45,6 +45,8 @@ xqc_h3_frm_reset_pctx(xqc_h3_frame_pctx_t *pctx) break; case XQC_H3_FRM_MAX_PUSH_ID: break; + case XQC_H3_EXT_FRM_BIDI_STREAM_TYPE: + break; case XQC_H3_FRM_UNKNOWN: break; } @@ -151,6 +153,16 @@ xqc_h3_frm_parse_max_push_id(const unsigned char *p, size_t sz, xqc_h3_frame_t * return pos - p; } +ssize_t +xqc_h3_ext_frm_parse_bidi_stream_type(const unsigned char *p, size_t sz, xqc_h3_frame_t *frame, xqc_bool_t *fin) +{ + const unsigned char *pos = p; + *fin = XQC_FALSE; + xqc_h3_ext_frame_bidi_stream_type_t *stream_type = &frame->frame_payload.stream_type; + XQC_H3_DECODE_DISCRETE_VINT_VALUE(pos, sz, stream_type->stream_type, fin); + return pos - p; +} + ssize_t xqc_h3_frm_parse_reserved(const unsigned char *p, size_t sz, xqc_h3_frame_t *frame, xqc_bool_t *fin) @@ -241,6 +253,10 @@ xqc_h3_frm_parse(const unsigned char *p, size_t sz, xqc_h3_frame_pctx_t *pctx) XQC_H3_DECODE_FRM(xqc_h3_frm_parse_max_push_id, pos, sz, pctx->frame, &fin); break; } + case XQC_H3_EXT_FRM_BIDI_STREAM_TYPE: { + XQC_H3_DECODE_FRM(xqc_h3_ext_frm_parse_bidi_stream_type, pos, sz, pctx->frame, &fin); + break; + } default: { XQC_H3_DECODE_FRM(xqc_h3_frm_parse_reserved, pos, sz, pctx->frame, &fin); break; @@ -512,3 +528,31 @@ xqc_h3_frm_write_max_push_id(xqc_list_head_t *send_buf, uint64_t push_id, uint8_ return XQC_OK; } + +xqc_int_t +xqc_h3_ext_frm_write_bidi_stream_type(xqc_list_head_t *send_buf, + uint64_t stream_type, uint8_t fin) +{ + size_t len = xqc_put_varint_len(stream_type); + xqc_var_buf_t *buf = xqc_var_buf_create(xqc_put_varint_len(XQC_H3_EXT_FRM_BIDI_STREAM_TYPE) + + xqc_put_varint_len(len) + + len); + if (buf == NULL) { + return -XQC_EMALLOC; + } + + unsigned char *pos = buf->data; + pos = xqc_put_varint(pos, XQC_H3_EXT_FRM_BIDI_STREAM_TYPE); + pos = xqc_put_varint(pos, len); + pos = xqc_put_varint(pos, stream_type); + buf->data_len = pos - buf->data; + buf->fin_flag = fin; + + xqc_int_t ret = xqc_list_buf_to_tail(send_buf, buf); + if (ret != XQC_OK) { + xqc_var_buf_free(buf); + return ret; + } + + return XQC_OK; +} diff --git a/src/http3/frame/xqc_h3_frame.h b/src/http3/frame/xqc_h3_frame.h index d85634f01..0e8811938 100644 --- a/src/http3/frame/xqc_h3_frame.h +++ b/src/http3/frame/xqc_h3_frame.h @@ -9,13 +9,14 @@ #include "src/http3/frame/xqc_h3_frame_defs.h" typedef union xqc_h3_frame_payload_s { - xqc_h3_frame_headers_t headers; - xqc_h3_frame_data_t data; - xqc_h3_frame_cancel_push_t cancel_push; - xqc_h3_frame_settings_t settings; - xqc_h3_frame_push_promise_t push_promise; - xqc_h3_frame_goaway_t goaway; - xqc_h3_frame_max_push_id_t max_push_id; + xqc_h3_frame_headers_t headers; + xqc_h3_frame_data_t data; + xqc_h3_frame_cancel_push_t cancel_push; + xqc_h3_frame_settings_t settings; + xqc_h3_frame_push_promise_t push_promise; + xqc_h3_frame_goaway_t goaway; + xqc_h3_frame_max_push_id_t max_push_id; + xqc_h3_ext_frame_bidi_stream_type_t stream_type; } xqc_h3_frame_pl_t; @@ -86,6 +87,8 @@ xqc_int_t xqc_h3_frm_write_goaway(xqc_list_head_t *send_buf, uint64_t push_id, u xqc_int_t xqc_h3_frm_write_max_push_id(xqc_list_head_t *send_buf, uint64_t push_id, uint8_t fin); +xqc_int_t xqc_h3_ext_frm_write_bidi_stream_type(xqc_list_head_t *send_buf, uint64_t stream_type, uint8_t fin); + void xqc_h3_frm_reset_pctx(xqc_h3_frame_pctx_t *pctx); #endif diff --git a/src/http3/frame/xqc_h3_frame_defs.h b/src/http3/frame/xqc_h3_frame_defs.h index 883125c9b..74f9f367d 100644 --- a/src/http3/frame/xqc_h3_frame_defs.h +++ b/src/http3/frame/xqc_h3_frame_defs.h @@ -9,14 +9,18 @@ typedef enum xqc_h3_frm_type_s { - XQC_H3_FRM_DATA = 0x00, - XQC_H3_FRM_HEADERS = 0x01, - XQC_H3_FRM_CANCEL_PUSH = 0x03, - XQC_H3_FRM_SETTINGS = 0x04, - XQC_H3_FRM_PUSH_PROMISE = 0x05, - XQC_H3_FRM_GOAWAY = 0x07, - XQC_H3_FRM_MAX_PUSH_ID = 0x0d, - XQC_H3_FRM_UNKNOWN = UINT64_MAX, + XQC_H3_FRM_DATA = 0x00, + XQC_H3_FRM_HEADERS = 0x01, + XQC_H3_FRM_CANCEL_PUSH = 0x03, + XQC_H3_FRM_SETTINGS = 0x04, + XQC_H3_FRM_PUSH_PROMISE = 0x05, + XQC_H3_FRM_GOAWAY = 0x07, + XQC_H3_FRM_MAX_PUSH_ID = 0x0d, + + /* extension */ + XQC_H3_EXT_FRM_BIDI_STREAM_TYPE = 0x20, + + XQC_H3_FRM_UNKNOWN = UINT64_MAX, } xqc_h3_frm_type_t; @@ -55,4 +59,8 @@ typedef struct xqc_h3_frm_max_push_id_t { xqc_discrete_int_pctx_t push_id; } xqc_h3_frame_max_push_id_t; +typedef struct xqc_h3_ext_frm_bidi_stream_type_s { + xqc_discrete_int_pctx_t stream_type; +} xqc_h3_ext_frame_bidi_stream_type_t; + #endif \ No newline at end of file diff --git a/src/http3/xqc_h3_conn.c b/src/http3/xqc_h3_conn.c index c9e838c09..d2a20cf5b 100644 --- a/src/http3/xqc_h3_conn.c +++ b/src/http3/xqc_h3_conn.c @@ -296,10 +296,13 @@ xqc_h3_conn_init_callbacks(xqc_h3_conn_t *h3c) xqc_int_t ret = xqc_h3_ctx_get_app_callbacks(&h3_cbs); if (XQC_OK != ret || h3_cbs == NULL) { xqc_log(h3c->log, XQC_LOG_ERROR, "|can't get app callbacks, not initialized?"); - return ret; + return -XQC_EFATAL; } h3c->h3_conn_callbacks = h3_cbs->h3c_cbs; + if (h3c->flags & XQC_H3_CONN_FLAG_EXT_ENABLED) { + h3c->h3_ext_dgram_callbacks = h3_cbs->h3_ext_dgram_cbs; + } return XQC_OK; } @@ -320,8 +323,14 @@ xqc_h3_conn_create(xqc_connection_t *conn, void *user_data) h3c->control_stream_out = NULL; + if (conn->engine->config->enable_h3_ext) { + h3c->flags |= XQC_H3_CONN_FLAG_EXT_ENABLED; + } + /* set callback functions from application layer to http3 layer */ - xqc_h3_conn_init_callbacks(h3c); + if (xqc_h3_conn_init_callbacks(h3c) != XQC_OK) { + h3c->flags &= ~XQC_H3_CONN_FLAG_EXT_ENABLED; + } h3c->local_h3_conn_settings = default_local_h3_conn_settings; h3c->peer_h3_conn_settings = default_peer_h3_conn_settings; @@ -744,3 +753,4 @@ const xqc_conn_callbacks_t h3_conn_callbacks = { .conn_handshake_finished = xqc_h3_conn_handshake_finished, .conn_ping_acked = xqc_h3_conn_ping_acked_notify, }; + diff --git a/src/http3/xqc_h3_conn.h b/src/http3/xqc_h3_conn.h index 566068f19..30f63fe6e 100644 --- a/src/http3/xqc_h3_conn.h +++ b/src/http3/xqc_h3_conn.h @@ -57,38 +57,44 @@ typedef enum { the upper layer of h3 connection creation */ XQC_H3_CONN_FLAG_UPPER_CONN_EXIST = 1 << 7, + /* is h3 ext enabled ? */ + XQC_H3_CONN_FLAG_EXT_ENABLED = 1 << 8, + } xqc_http3_conn_flag; typedef struct xqc_h3_conn_s { /* transport contexts */ - xqc_connection_t *conn; - xqc_log_t *log; - void *user_data; + xqc_connection_t *conn; + xqc_log_t *log; + void *user_data; /* h3 connection state flags */ - uint64_t flags; + uint64_t flags; /* h3 connection callback functions for user */ - xqc_h3_conn_callbacks_t h3_conn_callbacks; + xqc_h3_conn_callbacks_t h3_conn_callbacks; + + /* h3 datagram callback functions for user */ + xqc_h3_ext_dgram_callbacks_t h3_ext_dgram_callbacks; - uint64_t max_stream_id_recvd; - uint64_t goaway_stream_id; + uint64_t max_stream_id_recvd; + uint64_t goaway_stream_id; /* qpack context */ - xqc_qpack_t *qpack; + xqc_qpack_t *qpack; /* uni-streams */ - xqc_h3_stream_t *qdec_stream; - xqc_h3_stream_t *qenc_stream; - xqc_h3_stream_t *control_stream_out; + xqc_h3_stream_t *qdec_stream; + xqc_h3_stream_t *qenc_stream; + xqc_h3_stream_t *control_stream_out; /* blocked streams */ - xqc_list_head_t block_stream_head; - uint64_t block_stream_count; + xqc_list_head_t block_stream_head; + uint64_t block_stream_count; /* h3 settings */ - xqc_h3_conn_settings_t local_h3_conn_settings; /* set by user for sending to the peer */ - xqc_h3_conn_settings_t peer_h3_conn_settings; /* receive from peer */ + xqc_h3_conn_settings_t local_h3_conn_settings; /* set by user for sending to the peer */ + xqc_h3_conn_settings_t peer_h3_conn_settings; /* receive from peer */ } xqc_h3_conn_t; diff --git a/src/http3/xqc_h3_ctx.c b/src/http3/xqc_h3_ctx.c index 26e96071b..59a72c392 100644 --- a/src/http3/xqc_h3_ctx.c +++ b/src/http3/xqc_h3_ctx.c @@ -2,9 +2,12 @@ * @copyright Copyright (c) 2022, Alibaba Group Holding Limited */ +#include #include "xqc_h3_ctx.h" #include "xqc_h3_conn.h" #include "xqc_h3_stream.h" +#include "xqc_h3_ext_dgram.h" +#include "src/transport/xqc_engine.h" /* 应用层注册回调,放到engine */ typedef struct xqc_h3_ctx_s { @@ -34,17 +37,29 @@ xqc_h3_ctx_init(xqc_engine_t *engine, xqc_h3_callbacks_t *h3_cbs) /* init http3 layer callbacks */ xqc_app_proto_callbacks_t ap_cbs = { .conn_cbs = h3_conn_callbacks, - .stream_cbs = h3_stream_callbacks + .stream_cbs = h3_stream_callbacks, }; /* register ALPN and transport layer callbacks */ - if (xqc_engine_register_alpn(engine, XQC_ALPN_H3, XQC_ALPN_H3_LEN, &ap_cbs) != XQC_OK - || xqc_engine_register_alpn(engine, XQC_ALPN_H3_29, XQC_ALPN_H3_29_LEN, &ap_cbs) != XQC_OK) + if (xqc_engine_register_alpn(engine, XQC_ALPN_H3, strlen(XQC_ALPN_H3), &ap_cbs) != XQC_OK + || xqc_engine_register_alpn(engine, XQC_ALPN_H3_29, strlen(XQC_ALPN_H3_29), &ap_cbs) != XQC_OK) { xqc_h3_ctx_destroy(engine); return -XQC_EFATAL; } + if (engine->config->enable_h3_ext) { + + ap_cbs.dgram_cbs = h3_ext_datagram_callbacks; + + /* register h3-ext ALPN */ + if (xqc_engine_register_alpn(engine, XQC_ALPN_H3_EXT, strlen(XQC_ALPN_H3_EXT), &ap_cbs) != XQC_OK) + { + xqc_h3_ctx_destroy(engine); + return -XQC_EFATAL; + } + } + return XQC_OK; } @@ -52,8 +67,9 @@ xqc_h3_ctx_init(xqc_engine_t *engine, xqc_h3_callbacks_t *h3_cbs) xqc_int_t xqc_h3_ctx_destroy(xqc_engine_t *engine) { - xqc_engine_unregister_alpn(engine, XQC_ALPN_H3_29, XQC_ALPN_H3_29_LEN); - xqc_engine_unregister_alpn(engine, XQC_ALPN_H3, XQC_ALPN_H3_LEN); + xqc_engine_unregister_alpn(engine, XQC_ALPN_H3_29, strlen(XQC_ALPN_H3_29)); + xqc_engine_unregister_alpn(engine, XQC_ALPN_H3, strlen(XQC_ALPN_H3)); + xqc_engine_unregister_alpn(engine, XQC_ALPN_H3_EXT, strlen(XQC_ALPN_H3_EXT)); if (h3_ctx) { xqc_free(h3_ctx); diff --git a/src/http3/xqc_h3_defs.c b/src/http3/xqc_h3_defs.c index 32f842e4b..b2d724568 100644 --- a/src/http3/xqc_h3_defs.c +++ b/src/http3/xqc_h3_defs.c @@ -12,3 +12,10 @@ const char *const xqc_h3_alpn[] = { [XQC_IDRAFT_VER_29] = XQC_ALPN_H3_29, /* draft-29 */ [XQC_IDRAFT_VER_NEGOTIATION] = "", }; + +const char *const xqc_h3_ext_alpn[] = { + [XQC_IDRAFT_INIT_VER] = "", /* placeholder */ + [XQC_VERSION_V1] = XQC_ALPN_H3_EXT, /* QUIC v1 */ + [XQC_IDRAFT_VER_29] = "", /* draft-29 */ + [XQC_IDRAFT_VER_NEGOTIATION] = "", +}; \ No newline at end of file diff --git a/src/http3/xqc_h3_defs.h b/src/http3/xqc_h3_defs.h index 818d461c5..c0fb93ba2 100644 --- a/src/http3/xqc_h3_defs.h +++ b/src/http3/xqc_h3_defs.h @@ -5,13 +5,14 @@ #ifndef _XQC_H3_DEFS_H_ #define _XQC_H3_DEFS_H_ +#include + /** * ALPN definitions */ -#define XQC_ALPN_H3 "h3" -#define XQC_ALPN_H3_LEN 2 -#define XQC_ALPN_H3_29 "h3-29" -#define XQC_ALPN_H3_29_LEN 5 +#define XQC_ALPN_H3 XQC_DEFINED_ALPN_H3 +#define XQC_ALPN_H3_29 XQC_DEFINED_ALPN_H3_29 +#define XQC_ALPN_H3_EXT XQC_DEFINED_ALPN_H3_EXT extern const char *const xqc_h3_alpn[]; diff --git a/src/http3/xqc_h3_ext_bytestream.c b/src/http3/xqc_h3_ext_bytestream.c new file mode 100644 index 000000000..1770b66c9 --- /dev/null +++ b/src/http3/xqc_h3_ext_bytestream.c @@ -0,0 +1,725 @@ +#include +#include +#include "src/transport/xqc_engine.h" +#include "src/transport/xqc_stream.h" +#include "src/common/xqc_log.h" +#include "src/http3/xqc_h3_ext_bytestream.h" +#include "src/http3/xqc_h3_stream.h" +#include "src/common/xqc_common.h" +#include "src/http3/xqc_h3_ctx.h" + +typedef enum { + XQC_H3_EXT_BYTESTREAM_FLAG_FIN_RCVD = 1, + XQC_H3_EXT_BYTESTREAM_FLAG_FIN_SENT = 1 << 1, + XQC_H3_EXT_BYTESTREAM_FLAG_FIN_READ = 1 << 2, + XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST = 1 << 3, +} xqc_h3_ext_bytestream_flag_t; + +typedef struct xqc_h3_ext_bytestream_s { + + /* h3 stream handler */ + xqc_h3_stream_t *h3_stream; + + /* user data for request callback */ + void *user_data; + + /* bytestream callback */ + xqc_h3_ext_bytestream_callbacks_t *bs_callbacks; + + /* flag */ + xqc_h3_ext_bytestream_flag_t flag; + + /* received body buf list and statistic information */ + xqc_list_head_t data_buf_list; + uint64_t data_buf_cnt; + + /* var_buf */ + xqc_var_buf_t *msg_buf; + + /* statistic */ + size_t bytes_rcvd; + size_t bytes_sent; + const char *stream_close_msg; + xqc_usec_t create_time; + xqc_usec_t fin_rcvd_time; + xqc_usec_t fin_read_time; + xqc_usec_t fin_sent_time; + xqc_usec_t fin_acked_time; + xqc_usec_t first_byte_sent_time; + xqc_usec_t first_byte_rcvd_time; + +} xqc_h3_ext_bytestream_t; + +xqc_h3_ext_bytestream_data_buf_t* +xqc_h3_ext_bytestream_create_data_buf(xqc_h3_frame_pctx_t *pctx) +{ + xqc_h3_ext_bytestream_data_buf_t *new_buf = xqc_calloc(1, sizeof(xqc_h3_ext_bytestream_data_buf_t)); + if (new_buf) { + xqc_init_list_head(&new_buf->list); + xqc_init_list_head(&new_buf->buf_list); + new_buf->total_len = pctx->frame.len; + new_buf->curr_len = 0; + } + return new_buf; +} + +void +xqc_h3_ext_bytestream_free_data_buf(xqc_h3_ext_bytestream_data_buf_t *buf) +{ + if (buf) { + xqc_list_buf_list_free(&buf->buf_list); + xqc_list_del_init(&buf->list); + xqc_free(buf); + } +} + +xqc_h3_ext_bytestream_data_buf_t* +xqc_h3_ext_bytestream_get_last_data_buf(xqc_h3_ext_bytestream_t *bs, xqc_h3_frame_pctx_t *pctx) +{ + if (bs->data_buf_cnt > 0) { + xqc_list_head_t *tail_node; + xqc_h3_ext_bytestream_data_buf_t *tail_buf; + tail_node = bs->data_buf_list.prev; + tail_buf = xqc_list_entry(tail_node, xqc_h3_ext_bytestream_data_buf_t, list); + if (tail_buf->curr_len < tail_buf->total_len) { + return tail_buf; + } + } + + /* create a new buf */ + xqc_h3_ext_bytestream_data_buf_t *new_buf = xqc_h3_ext_bytestream_create_data_buf(pctx); + if (new_buf) { + bs->data_buf_cnt++; + xqc_list_add_tail(&new_buf->list, &bs->data_buf_list); + new_buf->start_time = xqc_monotonic_timestamp(); + } + return new_buf; +} + +xqc_int_t +xqc_h3_ext_bytestream_save_data_to_buf(xqc_h3_ext_bytestream_data_buf_t *buf, + const uint8_t *data, size_t data_len) +{ + /* sanity check */ + if (buf->curr_len + data_len > buf->total_len) { + return -XQC_H3_DECODE_ERROR; + } + + xqc_int_t ret = XQC_OK; + xqc_var_buf_t *v_buf = xqc_var_buf_create_with_limit(data_len, data_len); + if (!v_buf) { + return -XQC_EMALLOC; + } + + ret = xqc_list_buf_to_tail(&buf->buf_list, v_buf); + if (ret != XQC_OK) { + xqc_var_buf_free(v_buf); + return -XQC_EMALLOC; + } + + xqc_var_buf_save_data(v_buf, data, data_len); + buf->curr_len += data_len; + buf->buf_cnt++; + + return XQC_OK; +} + +xqc_var_buf_t* +xqc_h3_ext_bytestream_data_buf_merge(xqc_h3_ext_bytestream_data_buf_t *buf) +{ + xqc_var_buf_t *v_buf = NULL; + xqc_list_buf_t *segment; + + /* optimization: reduce memcpy for short messages */ + if (buf->buf_cnt == 1) { + segment = xqc_list_entry(buf->buf_list.next, xqc_list_buf_t, list_head); + v_buf = segment->buf; + segment->buf = NULL; + xqc_list_buf_free(segment); + buf->buf_cnt--; + return v_buf; + } + + v_buf = xqc_var_buf_create_with_limit(buf->total_len, buf->total_len); + if (!v_buf) { + return NULL; + } + + xqc_list_head_t *pos, *next; + xqc_list_for_each_safe(pos, next, &buf->buf_list) { + segment = xqc_list_entry(pos, xqc_list_buf_t, list_head); + xqc_var_buf_save_data(v_buf, segment->buf->data, segment->buf->data_len); + xqc_list_buf_free(segment); + buf->buf_cnt--; + } + return v_buf; +} + +xqc_int_t +xqc_h3_ext_bytestream_init_callbacks(xqc_h3_ext_bytestream_t *bs) +{ + xqc_h3_callbacks_t *h3_cbs = NULL; + xqc_int_t ret = xqc_h3_ctx_get_app_callbacks(&h3_cbs); + if (XQC_OK != ret || h3_cbs == NULL) { + xqc_log(bs->h3_stream->log, XQC_LOG_ERROR, "|can't get app callbacks, not registered ?|"); + return ret; + } + + bs->bs_callbacks = &h3_cbs->h3_ext_bs_cbs; + return XQC_OK; +} + +xqc_h3_ext_bytestream_t* +xqc_h3_ext_bytestream_create_inner(xqc_h3_conn_t *h3_conn, + xqc_h3_stream_t *h3_stream, void *user_data) +{ + xqc_h3_ext_bytestream_t *bs; + bs = xqc_calloc(1, sizeof(xqc_h3_ext_bytestream_t)); + if (!bs) { + xqc_log(h3_conn->log, XQC_LOG_ERROR, "|xqc_calloc error|"); + return NULL; + } + + if (xqc_h3_ext_bytestream_init_callbacks(bs) != XQC_OK) { + xqc_free(bs); + return NULL; + } + + bs->h3_stream = h3_stream; + bs->user_data = user_data; + bs->flag = 0; + bs->create_time = xqc_monotonic_timestamp(); + bs->msg_buf = NULL; + + xqc_init_list_head(&bs->data_buf_list); + bs->data_buf_cnt = 0; + + return bs; +} + + +xqc_h3_ext_bytestream_t* +xqc_h3_ext_bytestream_create(xqc_engine_t *engine, + const xqc_cid_t *cid, void *user_data) +{ + xqc_stream_t *stream; + xqc_h3_stream_t *h3_stream; + xqc_h3_ext_bytestream_t *h3_ext_bs; + xqc_h3_conn_t *h3_conn; + int ret; + + stream = xqc_stream_create(engine, cid, NULL); + if (!stream) { + xqc_log(engine->log, XQC_LOG_ERROR, "|xqc_stream_create error|"); + return NULL; + } + + h3_conn = (xqc_h3_conn_t*)stream->stream_conn->proto_data; + + if (!(h3_conn->flags & XQC_H3_CONN_FLAG_EXT_ENABLED)) { + // it is safe to destroy the stream here, as it is not notified to upper layer. + xqc_destroy_stream(stream); + xqc_log(engine->log, XQC_LOG_ERROR, "|try to create bytestream while it is disabled on the connection|"); + return NULL; + } + + h3_stream = xqc_h3_stream_create(h3_conn, stream, XQC_H3_STREAM_TYPE_BYTESTEAM, user_data); + if (!h3_stream) { + // it is safe to destroy the stream here, as it is not notified to upper layer. + xqc_destroy_stream(stream); + xqc_log(engine->log, XQC_LOG_ERROR, "|xqc_h3_stream_create error|"); + return NULL; + } + + h3_ext_bs = xqc_h3_ext_bytestream_create_inner(h3_conn, h3_stream, user_data); + if (!h3_ext_bs) { + // it is safe to destroy the stream here, as it is not notified to upper layer. + xqc_destroy_stream(stream); + // the h3_stream will be destroyed in the close_notify triggered by xqc_destroy_stream + xqc_log(engine->log, XQC_LOG_ERROR, "|xqc_h3_ext_bytestream_create_inner error|"); + return NULL; + } + + h3_stream->h3_ext_bs = h3_ext_bs; + + // the initiator of the bytestream should send bidi_stream_type frame + if (xqc_h3_stream_send_bidi_stream_type(h3_stream, XQC_H3_BIDI_STREAM_TYPE_BYTESTREAM, 0) != XQC_OK) { + // it is safe to destroy the stream here, as it is not notified to upper layer. + xqc_destroy_stream(stream); + // the h3_stream will be destroyed in the close_notify triggered by xqc_destroy_stream + // the h3_ext_bytestream will be destroyed when h3_stream is destroyed + xqc_log(engine->log, XQC_LOG_ERROR, "|send bidi_stream_type frame error|"); + return NULL; + } + + if (h3_ext_bs->bs_callbacks->bs_create_notify + && !(h3_ext_bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST)) + { + ret = h3_ext_bs->bs_callbacks->bs_create_notify(h3_ext_bs, h3_ext_bs->user_data); + if (ret < 0) { + xqc_log(engine->log, XQC_LOG_INFO, "|app create callback error|"); + } + + } + + h3_ext_bs->flag |= XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST; + + xqc_log(engine->log, XQC_LOG_DEBUG, "|success|stream_id:%ui|conn:%p|conn_state:%s|flag:%s|", + h3_stream->stream_id, h3_conn->conn, xqc_conn_state_2_str(h3_conn->conn->conn_state), + xqc_conn_flag_2_str(h3_conn->conn->conn_flag)); + + return h3_ext_bs; +} + +xqc_h3_ext_bytestream_t* +xqc_h3_ext_bytestream_create_passive(xqc_h3_conn_t *h3_conn, + xqc_h3_stream_t *h3_stream, void *user_data) +{ + xqc_h3_ext_bytestream_t *h3_ext_bs = xqc_h3_ext_bytestream_create_inner(h3_conn, h3_stream, user_data); + int ret; + if (!h3_ext_bs) { + xqc_log(h3_conn->log, XQC_LOG_ERROR, "|xqc_h3_ext_bytestream_create_inner error|"); + return NULL; + } + + h3_stream->h3_ext_bs = h3_ext_bs; + + if (h3_ext_bs->bs_callbacks->bs_create_notify + && !(h3_ext_bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST)) + { + + ret = h3_ext_bs->bs_callbacks->bs_create_notify(h3_ext_bs, h3_ext_bs->user_data); + if (ret < 0) { + xqc_log(h3_conn->log, XQC_LOG_INFO, "|app create notify error|%d|", ret); + } + } + + h3_ext_bs->flag |= XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST; + + return h3_ext_bs; +} + +void +xqc_h3_ext_bytestream_destroy(xqc_h3_ext_bytestream_t *bs) +{ + xqc_h3_stream_t *h3s = bs->h3_stream; + + /* print request statistic log */ + xqc_h3_ext_bytestream_stats_t stats = xqc_h3_ext_bytestream_get_stats(bs); + + xqc_log(h3s->log, XQC_LOG_REPORT, "|stream_id:%ui|close_msg:%s|err:%d" + "|bytes_sent:%uz|bytes_rcvd:%uz|create_time:%ui|fb_sent_delay:%ui|fb_rcvd_delay:%ui" + "|fin_sent_delay:%ui|fin_acked_delay:%ui|fin_rcvd_delay:%ui|", + xqc_h3_ext_bytestream_id(bs), stats.stream_close_msg ? : "", + stats.stream_err, stats.bytes_sent, stats.bytes_rcvd, + stats.create_time, + xqc_calc_delay(stats.first_byte_sent_time, stats.create_time), + xqc_calc_delay(stats.first_byte_rcvd_time, stats.create_time), + xqc_calc_delay(stats.fin_sent_time, stats.create_time), + xqc_calc_delay(stats.fin_acked_time, stats.create_time), + xqc_calc_delay(stats.fin_rcvd_time, stats.create_time)); + + if (bs->bs_callbacks->bs_close_notify + && (bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST)) + { + bs->bs_callbacks->bs_close_notify(bs, bs->user_data); + bs->flag &= ~XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST; + } + + xqc_list_head_t *pos, *next; + xqc_h3_ext_bytestream_data_buf_t *buf; + xqc_list_for_each_safe(pos, next, &bs->data_buf_list) { + buf = xqc_list_entry(pos, xqc_h3_ext_bytestream_data_buf_t, list); + xqc_h3_ext_bytestream_free_data_buf(buf); + } + + if (bs->msg_buf) { + xqc_var_buf_free(bs->msg_buf); + } + + xqc_free(bs); +} + +xqc_int_t +xqc_h3_ext_bytestream_close(xqc_h3_ext_bytestream_t *h3_ext_bs) +{ + xqc_connection_t *conn = h3_ext_bs->h3_stream->h3c->conn; + xqc_h3_stream_t *h3s = h3_ext_bs->h3_stream; + + xqc_int_t ret = xqc_h3_stream_close(h3s); + if (ret) { + xqc_log(conn->log, XQC_LOG_ERROR, "|fail|ret:%d|stream_id:%ui|conn:%p|conn_state:%s|" + "flag:%s|", ret, h3s->stream_id, conn, xqc_conn_state_2_str(conn->conn_state), + xqc_conn_flag_2_str(conn->conn_flag)); + return ret; + } + + xqc_log(conn->log, XQC_LOG_DEBUG, "|success|stream_id:%ui|conn:%p|conn_state:%s|flag:%s|", + h3s->stream_id, conn, xqc_conn_state_2_str(conn->conn_state), + xqc_conn_flag_2_str(conn->conn_flag)); + + return XQC_OK; +} + + +ssize_t +xqc_h3_ext_bytestream_finish(xqc_h3_ext_bytestream_t *h3_ext_bs) +{ + xqc_int_t ret = 0; + if (h3_ext_bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_FIN_SENT) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_DEBUG, "|already sent fin|"); + return XQC_OK; + } + ret = xqc_h3_ext_bytestream_send(h3_ext_bs, NULL, 0, 1, XQC_DATA_QOS_HIGHEST); + if (ret == 0) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_DEBUG, "|send pure fin|"); + } + + return ret; +} + + +void +xqc_h3_ext_bytestream_set_user_data(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *user_data) +{ + h3_ext_bs->user_data = user_data; +} + + + +void* +xqc_h3_ext_bytestream_get_user_data(xqc_h3_ext_bytestream_t *h3_ext_bs) +{ + return h3_ext_bs->user_data; +} + + +void +xqc_h3_ext_bytestream_save_stats_from_stream(xqc_h3_ext_bytestream_t *bs, + xqc_stream_t *stream) +{ + bs->fin_acked_time = stream->stream_stats.first_fin_ack_time; + bs->stream_close_msg = stream->stream_close_msg; +} + + +xqc_h3_ext_bytestream_stats_t +xqc_h3_ext_bytestream_get_stats(xqc_h3_ext_bytestream_t *h3_ext_bs) +{ + xqc_h3_ext_bytestream_stats_t stats; + stats.create_time = h3_ext_bs->create_time; + stats.bytes_rcvd = h3_ext_bs->bytes_rcvd; + stats.bytes_sent = h3_ext_bs->bytes_sent; + stats.fin_sent_time = h3_ext_bs->fin_sent_time; + stats.fin_acked_time = h3_ext_bs->fin_acked_time; + stats.fin_rcvd_time = h3_ext_bs->fin_rcvd_time; + stats.fin_read_time = h3_ext_bs->fin_read_time; + stats.first_byte_rcvd_time = h3_ext_bs->first_byte_rcvd_time; + stats.first_byte_sent_time = h3_ext_bs->first_byte_sent_time; + stats.stream_close_msg = h3_ext_bs->stream_close_msg; + stats.stream_err = xqc_h3_stream_get_err(h3_ext_bs->h3_stream); + return stats; +} + + +ssize_t +xqc_h3_ext_bytestream_send(xqc_h3_ext_bytestream_t *h3_ext_bs, + unsigned char *data, size_t data_size, uint8_t fin, + xqc_data_qos_level_t qos_level) +{ + /* data_size is allowed if it's fin only */ + if (data_size > 0 && data == NULL) { + return -XQC_H3_EPARAM; + } + + if (h3_ext_bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_FIN_SENT) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_WARN, + "|send data after FIN sent|stream_id:%ui|", + xqc_h3_ext_bytestream_id(h3_ext_bs)); + return -XQC_H3_BYTESTREAM_FIN_SENT; + } + + if (h3_ext_bs->msg_buf) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_DEBUG, + "|msg_buf_has_blocked_data|stream_id:%ui|data_size:%uz|fin:%ud|", + h3_ext_bs->h3_stream->stream_id, data_size, (unsigned int)fin); + return -XQC_EAGAIN; + } + + ssize_t sent = xqc_h3_stream_send_data(h3_ext_bs->h3_stream, data, data_size, fin); + + if (sent < 0 && sent != -XQC_EAGAIN) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_ERROR, + "|xqc_h3_stream_send_data error|stream_id:%ui|ret:%z|data_size:%z|fin:%d|", + h3_ext_bs->h3_stream->stream_id, sent, data_size, fin); + return sent; + } + + if (sent >= 0) { + h3_ext_bs->bytes_sent += sent; + if (fin && sent == data_size) { + xqc_h3_ext_bytestream_fin_sent(h3_ext_bs); + xqc_h3_ext_bytestream_set_fin_sent_flag(h3_ext_bs); + } + + xqc_h3_ext_bytestream_send_begin(h3_ext_bs); + } + + if ((sent == -XQC_EAGAIN) || (sent >= 0 && sent != data_size)) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_DEBUG, + "|xqc_h3_stream_send_data blocked|stream_id:%ui|data_size:%uz|fin:%ud|", + h3_ext_bs->h3_stream->stream_id, data_size, (unsigned int)fin); + /* create a msg buffer for the current msg */ + if (h3_ext_bs->msg_buf) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_ERROR, + "|msg_buf_already_exist|stream_id:%ui|data_size:%uz|fin:%ud|sent:%z|", + h3_ext_bs->h3_stream->stream_id, data_size, (unsigned int)fin, sent); + XQC_H3_CONN_ERR(h3_ext_bs->h3_stream->h3c, H3_INTERNAL_ERROR, -XQC_H3_BYTESTREAM_MSG_BUF_EXIST); + return -XQC_H3_BYTESTREAM_MSG_BUF_EXIST; + } + + sent = sent == -XQC_EAGAIN ? 0 : sent; + h3_ext_bs->msg_buf = xqc_var_buf_create(data_size - sent); + + if (!h3_ext_bs->msg_buf) { + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_ERROR, + "|malloc_msg_buffer_failed|stream_id:%ui|data_size:%uz|fin:%ud|buf_sz:%z|", + h3_ext_bs->h3_stream->stream_id, data_size, (unsigned int)fin, data_size - sent); + XQC_H3_CONN_ERR(h3_ext_bs->h3_stream->h3c, H3_INTERNAL_ERROR, -XQC_H3_EMALLOC); + return -XQC_H3_EMALLOC; + } + + xqc_var_buf_save_data(h3_ext_bs->msg_buf, data + sent, data_size - sent); + h3_ext_bs->msg_buf->fin_flag = fin; + + return data_size; + } + + xqc_log(h3_ext_bs->h3_stream->log, XQC_LOG_DEBUG, "|stream_id:%ui|data_size:%uz|sent:%z|" + "total_bytes_sent:%uz|fin:%ud|conn:%p|", + xqc_h3_ext_bytestream_id(h3_ext_bs), data_size, sent, h3_ext_bs->bytes_sent, + (unsigned int)fin, h3_ext_bs->h3_stream->h3c->conn); + + return sent; +} + +xqc_stream_id_t +xqc_h3_ext_bytestream_id(xqc_h3_ext_bytestream_t *h3_ext_bs) +{ + return h3_ext_bs->h3_stream->stream_id; +} + + +xqc_h3_conn_t* +xqc_h3_ext_bytestream_get_h3_conn(xqc_h3_ext_bytestream_t *h3_ext_bs) +{ + return h3_ext_bs->h3_stream->h3c; +} + +xqc_int_t +xqc_h3_ext_bytestream_append_data_buf(xqc_h3_ext_bytestream_t *bs, + xqc_var_buf_t *buf) +{ + xqc_int_t ret = XQC_OK; + ret = xqc_list_buf_to_tail(&bs->data_buf_list, buf); + if (ret == XQC_OK) { + bs->data_buf_cnt++; + } + return ret; +} + +#define XQC_H3_EXT_BYTESTREAM_RECORD_TIME(a) \ + if ((a) == 0) { \ + (a) = xqc_monotonic_timestamp(); \ + } \ + +void +xqc_h3_ext_bytestream_recv_begin(xqc_h3_ext_bytestream_t *bs) +{ + XQC_H3_EXT_BYTESTREAM_RECORD_TIME(bs->first_byte_rcvd_time); +} + +void +xqc_h3_ext_bytestream_send_begin(xqc_h3_ext_bytestream_t *bs) +{ + XQC_H3_EXT_BYTESTREAM_RECORD_TIME(bs->first_byte_sent_time); +} + +void +xqc_h3_ext_bytestream_fin_rcvd(xqc_h3_ext_bytestream_t *bs) +{ + XQC_H3_EXT_BYTESTREAM_RECORD_TIME(bs->fin_rcvd_time); +} + +void +xqc_h3_ext_bytestream_fin_read(xqc_h3_ext_bytestream_t *bs) +{ + XQC_H3_EXT_BYTESTREAM_RECORD_TIME(bs->fin_read_time); +} + +void +xqc_h3_ext_bytestream_fin_sent(xqc_h3_ext_bytestream_t *bs) +{ + XQC_H3_EXT_BYTESTREAM_RECORD_TIME(bs->fin_sent_time); +} + + +xqc_int_t +xqc_h3_ext_bytestream_notify_write(xqc_h3_ext_bytestream_t *bs) +{ + xqc_int_t ret = XQC_OK; + xqc_bool_t msg_buf_done = XQC_FALSE; + + /* send msg buf first */ + if (bs->msg_buf) { + ssize_t sent = xqc_h3_stream_send_data(bs->h3_stream, + bs->msg_buf->data + bs->msg_buf->consumed_len, + bs->msg_buf->data_len - bs->msg_buf->consumed_len, + bs->msg_buf->fin_flag); + if (XQC_UNLIKELY(sent == -XQC_EAGAIN)) { + return XQC_OK; + + } else if (XQC_UNLIKELY(sent < 0)) { + xqc_log(bs->h3_stream->h3c->log, XQC_LOG_ERROR, + "|send_msg_buf_err|stream_id:%ui|msg_sz:%z|fin_flag:%d|ret:%z|", + xqc_h3_ext_bytestream_id(bs), + bs->msg_buf->data_len - bs->msg_buf->consumed_len, + bs->msg_buf->fin_flag, + sent); + return sent; + } + + bs->msg_buf->consumed_len += sent; + bs->bytes_sent += sent; + + if (bs->msg_buf->data_len == bs->msg_buf->consumed_len) { + if (bs->msg_buf->fin_flag) { + xqc_h3_ext_bytestream_fin_sent(bs); + xqc_h3_ext_bytestream_set_fin_sent_flag(bs); + } + xqc_var_buf_free(bs->msg_buf); + bs->msg_buf = NULL; + } + + xqc_h3_ext_bytestream_send_begin(bs); + } + + if (bs->bs_callbacks->bs_write_notify + && (bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST)) + { + ret = bs->bs_callbacks->bs_write_notify(bs, bs->user_data); + } + return ret; +} + +void +xqc_h3_ext_bytestream_set_fin_sent_flag(xqc_h3_ext_bytestream_t *bs) +{ + bs->flag |= XQC_H3_EXT_BYTESTREAM_FLAG_FIN_SENT; +} + +void +xqc_h3_ext_bytestream_set_fin_rcvd_flag(xqc_h3_ext_bytestream_t *bs) +{ + bs->flag |= XQC_H3_EXT_BYTESTREAM_FLAG_FIN_RCVD; +} + +xqc_bool_t +xqc_h3_ext_bytestream_should_notify_read(xqc_h3_ext_bytestream_t *bs) +{ + if(!xqc_list_empty(&bs->data_buf_list) + || ((bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_FIN_RCVD) + && !(bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_FIN_READ))) + { + return XQC_TRUE; + } + return XQC_FALSE; +} + +xqc_int_t +xqc_h3_ext_bytestream_notify_read(xqc_h3_ext_bytestream_t *bs) +{ + xqc_int_t ret = XQC_OK; + xqc_list_head_t *node, *next; + xqc_h3_ext_bytestream_data_buf_t *buf; + xqc_var_buf_t *merged_buf; + size_t data_sz; + uint8_t fin = 0; + + xqc_list_for_each_safe(node, next, &bs->data_buf_list) { + buf = xqc_list_entry(node, xqc_h3_ext_bytestream_data_buf_t, list); + + if (buf->end_time) { + if (bs->data_buf_cnt == 1 + && (bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_FIN_RCVD)) + { + fin = 1; + bs->flag |= XQC_H3_EXT_BYTESTREAM_FLAG_FIN_READ; + xqc_h3_ext_bytestream_fin_read(bs); + } + ret = XQC_OK; + data_sz = buf->total_len; + + merged_buf = xqc_h3_ext_bytestream_data_buf_merge(buf); + if (merged_buf) { + if (bs->bs_callbacks->bs_read_notify + && (bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST)) + { + ret = bs->bs_callbacks->bs_read_notify(bs, merged_buf->data, + merged_buf->data_len, + fin, + bs->user_data, + buf->end_time - buf->start_time); + } + + + bs->bytes_rcvd += data_sz; + xqc_log(bs->h3_stream->h3c->log, XQC_LOG_DEBUG, + "|msg_read_notify|stream_id:%ui|msg_sz:%z|fin:%d|rcv_start:%ui|rcv_end:%ui|", + xqc_h3_ext_bytestream_id(bs), data_sz, fin, buf->start_time, buf->end_time); + + } else { + ret = -XQC_EMALLOC; + xqc_log(bs->h3_stream->h3c->log, XQC_LOG_ERROR, "|merge_data_buf_error|"); + } + + xqc_h3_ext_bytestream_free_data_buf(buf); + bs->data_buf_cnt--; + if (ret < 0) { + xqc_log(bs->h3_stream->h3c->log, XQC_LOG_ERROR, "|bs_read_notify_err|stream_id:%ui|ret:%d|data_sz:%z|fin:%d|", + xqc_h3_ext_bytestream_id(bs), ret, data_sz, fin); + return ret; + } + } + } + + /* fin only */ + if (bs->data_buf_cnt == 0 + && ((bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_FIN_RCVD) + && !(bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_FIN_READ))) + { + bs->flag |= XQC_H3_EXT_BYTESTREAM_FLAG_FIN_READ; + xqc_h3_ext_bytestream_fin_read(bs); + ret = XQC_OK; + + if (bs->bs_callbacks->bs_read_notify + && (bs->flag & XQC_H3_EXT_BYTESTREAM_FLAG_UPPER_LAYER_EXIST)) + { + ret = bs->bs_callbacks->bs_read_notify(bs, NULL, + 0, + 1, + bs->user_data, + bs->fin_read_time - bs->fin_rcvd_time); + } + xqc_log(bs->h3_stream->h3c->log, XQC_LOG_DEBUG, + "|pure_fin_read_notify|stream_id:%ui|fin_rcv:%ui|fin_read:%ui|", + xqc_h3_ext_bytestream_id(bs), bs->fin_rcvd_time, bs->fin_read_time); + if (ret < 0) { + xqc_log(bs->h3_stream->h3c->log, XQC_LOG_ERROR, "|bs_read_notify_app_err|stream_id:%ui|ret:%d|data_sz:0|fin:1|", + xqc_h3_ext_bytestream_id(bs), ret); + } + } + + return ret; +} \ No newline at end of file diff --git a/src/http3/xqc_h3_ext_bytestream.h b/src/http3/xqc_h3_ext_bytestream.h new file mode 100644 index 000000000..b0b76b624 --- /dev/null +++ b/src/http3/xqc_h3_ext_bytestream.h @@ -0,0 +1,58 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#ifndef _XQC_H3_EXT_BYTESTREAM_H_INCLUDED_ +#define _XQC_H3_EXT_BYTESTREAM_H_INCLUDED_ + +#include +#include +#include "src/http3/xqc_h3_conn.h" +#include "src/http3/xqc_h3_stream.h" + +typedef struct xqc_h3_ext_bytestream_data_buf_s { + xqc_list_head_t list; + xqc_list_head_t buf_list; + uint32_t buf_cnt; + uint64_t total_len; + uint64_t curr_len; + xqc_usec_t start_time; + xqc_usec_t end_time; +} xqc_h3_ext_bytestream_data_buf_t; + +typedef struct xqc_h3_ext_bytestream_s xqc_h3_ext_bytestream_t; + +void xqc_h3_ext_bytestream_destroy(xqc_h3_ext_bytestream_t *bs); + +xqc_int_t xqc_h3_ext_bytestream_notify_write(xqc_h3_ext_bytestream_t *bs); + +xqc_h3_ext_bytestream_t *xqc_h3_ext_bytestream_create_passive( + xqc_h3_conn_t *h3_conn, xqc_h3_stream_t *h3_stream, void *user_data); + +xqc_int_t xqc_h3_ext_bytestream_append_data_buf(xqc_h3_ext_bytestream_t *bs, + xqc_var_buf_t *buf); + +xqc_h3_ext_bytestream_data_buf_t* xqc_h3_ext_bytestream_get_last_data_buf(xqc_h3_ext_bytestream_t *bs, xqc_h3_frame_pctx_t *pctx); +xqc_int_t xqc_h3_ext_bytestream_save_data_to_buf(xqc_h3_ext_bytestream_data_buf_t *buf, + const uint8_t *data, size_t data_len); + +void xqc_h3_ext_bytestream_set_fin_sent_flag(xqc_h3_ext_bytestream_t *bs); +void xqc_h3_ext_bytestream_set_fin_rcvd_flag(xqc_h3_ext_bytestream_t *bs); + +xqc_bool_t xqc_h3_ext_bytestream_should_notify_read(xqc_h3_ext_bytestream_t *bs); + +xqc_int_t xqc_h3_ext_bytestream_notify_read(xqc_h3_ext_bytestream_t *bs); + +void xqc_h3_ext_bytestream_save_stats_from_stream(xqc_h3_ext_bytestream_t *bs, + xqc_stream_t *stream); + +/* to record performance statistics */ +void xqc_h3_ext_bytestream_recv_begin(xqc_h3_ext_bytestream_t *bs); +void xqc_h3_ext_bytestream_send_begin(xqc_h3_ext_bytestream_t *bs); +void xqc_h3_ext_bytestream_fin_rcvd(xqc_h3_ext_bytestream_t *bs); +void xqc_h3_ext_bytestream_fin_read(xqc_h3_ext_bytestream_t *bs); +void xqc_h3_ext_bytestream_fin_sent(xqc_h3_ext_bytestream_t *bs); + + + +#endif \ No newline at end of file diff --git a/src/http3/xqc_h3_ext_dgram.c b/src/http3/xqc_h3_ext_dgram.c new file mode 100644 index 000000000..5f8ad537a --- /dev/null +++ b/src/http3/xqc_h3_ext_dgram.c @@ -0,0 +1,105 @@ +#include +#include +#include "src/http3/xqc_h3_ext_dgram.h" +#include "src/http3/xqc_h3_conn.h" + +void +xqc_h3_ext_datagram_read_notify(xqc_connection_t *conn, + void *user_data, const void *data, size_t data_len, uint64_t recv_time) +{ + xqc_h3_conn_t *h3c = (xqc_h3_conn_t*)conn->proto_data; + if (h3c->h3_ext_dgram_callbacks.dgram_read_notify + && (h3c->flags & XQC_H3_CONN_FLAG_UPPER_CONN_EXIST)) + { + h3c->h3_ext_dgram_callbacks.dgram_read_notify(h3c, + data, data_len, user_data, recv_time); + xqc_log(h3c->log, XQC_LOG_DEBUG, "|notify datagram read event to app|"); + } +} + +void +xqc_h3_ext_datagram_write_notify(xqc_connection_t *conn, + void *user_data) +{ + xqc_h3_conn_t *h3c = (xqc_h3_conn_t*)conn->proto_data; + if (h3c->h3_ext_dgram_callbacks.dgram_write_notify + && (h3c->flags & XQC_H3_CONN_FLAG_UPPER_CONN_EXIST)) + { + h3c->h3_ext_dgram_callbacks.dgram_write_notify(h3c, user_data); + xqc_log(h3c->log, XQC_LOG_DEBUG, + "|notify datagram write event to app|"); + } +} + +xqc_int_t +xqc_h3_ext_datagram_lost_notify(xqc_connection_t *conn, + uint64_t dgram_id, void *user_data) +{ + xqc_int_t ret = 0; + xqc_h3_conn_t *h3c = (xqc_h3_conn_t*)conn->proto_data; + if (h3c->h3_ext_dgram_callbacks.dgram_lost_notify + && (h3c->flags & XQC_H3_CONN_FLAG_UPPER_CONN_EXIST)) + { + ret = h3c->h3_ext_dgram_callbacks.dgram_lost_notify(h3c, dgram_id, user_data); + xqc_log(h3c->log, XQC_LOG_DEBUG, + "|notify lost datagram to app|dgram_id:%ui|", + dgram_id); + } + return ret; +} + +void +xqc_h3_ext_datagram_acked_notify(xqc_connection_t *conn, + uint64_t dgram_id, void *user_data) +{ + xqc_h3_conn_t *h3c = (xqc_h3_conn_t*)conn->proto_data; + if (h3c->h3_ext_dgram_callbacks.dgram_acked_notify + && (h3c->flags & XQC_H3_CONN_FLAG_UPPER_CONN_EXIST)) + { + h3c->h3_ext_dgram_callbacks.dgram_acked_notify(h3c, dgram_id, user_data); + xqc_log(h3c->log, XQC_LOG_DEBUG, + "|notify acked datagram to app|dgram_id:%ui|", + dgram_id); + } +} + +const xqc_datagram_callbacks_t h3_ext_datagram_callbacks = { + .datagram_read_notify = xqc_h3_ext_datagram_read_notify, + .datagram_write_notify = xqc_h3_ext_datagram_write_notify, + .datagram_lost_notify = xqc_h3_ext_datagram_lost_notify, + .datagram_acked_notify = xqc_h3_ext_datagram_acked_notify, +}; + +size_t +xqc_h3_ext_datagram_get_mss(xqc_h3_conn_t *conn) +{ + return xqc_datagram_get_mss(conn->conn); +} + +void +xqc_h3_ext_datagram_set_user_data(xqc_h3_conn_t *conn, void *user_data) +{ + xqc_datagram_set_user_data(conn->conn, user_data); +} + +void* +xqc_h3_ext_datagram_get_user_data(xqc_h3_conn_t *conn) +{ + return xqc_datagram_get_user_data(conn->conn); +} + +xqc_int_t +xqc_h3_ext_datagram_send(xqc_h3_conn_t *conn, void *data, + size_t data_len, uint64_t *dgram_id, xqc_data_qos_level_t qos_level) +{ + return xqc_datagram_send(conn->conn, data, data_len, dgram_id, qos_level); +} + +xqc_int_t +xqc_h3_ext_datagram_send_multiple(xqc_h3_conn_t *conn, + struct iovec *iov, uint64_t *dgram_id_list, size_t iov_size, + size_t *sent_cnt, size_t *sent_bytes, xqc_data_qos_level_t qos_level) +{ + return xqc_datagram_send_multiple(conn->conn, iov, dgram_id_list, iov_size, + sent_cnt, sent_bytes, qos_level); +} \ No newline at end of file diff --git a/src/http3/xqc_h3_ext_dgram.h b/src/http3/xqc_h3_ext_dgram.h new file mode 100644 index 000000000..3e48b4b41 --- /dev/null +++ b/src/http3/xqc_h3_ext_dgram.h @@ -0,0 +1,12 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#ifndef _XQC_H3_EXT_DGRAM_H_INCLUDED_ +#define _XQC_H3_EXT_DGRAM_H_INCLUDED_ + +#include + +extern const xqc_datagram_callbacks_t h3_ext_datagram_callbacks; + +#endif \ No newline at end of file diff --git a/src/http3/xqc_h3_stream.c b/src/http3/xqc_h3_stream.c index 5eb7f0629..fe10579ef 100644 --- a/src/http3/xqc_h3_stream.c +++ b/src/http3/xqc_h3_stream.c @@ -8,8 +8,7 @@ #include "src/transport/xqc_stream.h" #include "src/transport/xqc_engine.h" #include "src/http3/xqc_h3_conn.h" - - +#include "src/http3/xqc_h3_ext_bytestream.h" xqc_h3_stream_t * xqc_h3_stream_create(xqc_h3_conn_t *h3c, xqc_stream_t *stream, xqc_h3_stream_type_t type, @@ -85,10 +84,15 @@ xqc_h3_stream_destroy(xqc_h3_stream_t *h3s) h3s->blocked_stream = NULL; } - if (h3s->h3r) { + if (h3s->h3r && h3s->type == XQC_H3_STREAM_TYPE_REQUEST) { xqc_h3_request_destroy(h3s->h3r); } + //TODO: destroy bytestream + if (h3s->h3_ext_bs && h3s->type == XQC_H3_STREAM_TYPE_BYTESTEAM) { + xqc_h3_ext_bytestream_destroy(h3s->h3_ext_bs); + } + xqc_h3_frm_reset_pctx(&h3s->pctx.frame_pctx); xqc_qpack_destroy_req_ctx(h3s->ctx); xqc_list_buf_list_free(&h3s->send_buf); @@ -214,6 +218,7 @@ xqc_h3_stream_write_headers(xqc_h3_stream_t *h3s, xqc_http_headers_t *headers, u } +//TODO: remove obsoloted internal functions ssize_t xqc_h3_stream_write_data_to_buffer(xqc_h3_stream_t *h3s, unsigned char *data, uint64_t data_size, uint8_t fin) @@ -305,6 +310,29 @@ xqc_h3_stream_write_goaway_to_buffer(xqc_h3_stream_t *h3s, uint64_t push_id, uin return XQC_OK; } +xqc_int_t +xqc_h3_stream_write_bidi_stream_type_to_buffer(xqc_h3_stream_t *h3s, uint64_t stype, uint8_t fin) +{ + xqc_int_t ret = xqc_h3_ext_frm_write_bidi_stream_type(&h3s->send_buf, stype, fin); + if (ret != XQC_OK) { + xqc_log(h3s->log, XQC_LOG_ERROR, "|write BIDI_STREAM_TYPE frame error|%d|stream_id:%ui|fin:%d|", + ret, h3s->stream_id, (unsigned int)fin); + return ret; + } + xqc_log_event(h3s->log, HTTP_FRAME_CREATED, h3s, XQC_H3_EXT_FRM_BIDI_STREAM_TYPE, stype); + + ret = xqc_h3_stream_send_buffer(h3s); + if (ret < 0 && ret != -XQC_EAGAIN) { + xqc_log(h3s->log, XQC_LOG_ERROR, "|send BIDI_STREAM_TYPE frame error|%d|stream_id:%ui|fin:%d|", + ret, h3s->stream_id, (unsigned int)fin); + return ret; + } + + return XQC_OK; +} + + + ssize_t xqc_h3_stream_send_headers(xqc_h3_stream_t *h3s, xqc_http_headers_t *headers, uint8_t fin) @@ -357,6 +385,7 @@ xqc_h3_stream_send_data_frame(xqc_h3_stream_t *h3s, unsigned char *data, size_t xqc_int_t ret; uint8_t fin_only = fin && !data_size; uint8_t fin_only_sent = 0; + uint8_t fin_flag_for_quic_stream = 0; unsigned char *pos; /* Send buffered HEADERS frame if any */ @@ -364,6 +393,8 @@ xqc_h3_stream_send_data_frame(xqc_h3_stream_t *h3s, unsigned char *data, size_t if (ret < 0) { return ret; } + + xqc_log(h3s->log, XQC_LOG_DEBUG, "|xqc_h3_stream_send_buffer|success|"); do { if (h3s->data_frame.data_sent > h3s->data_frame.data_len) { @@ -383,6 +414,17 @@ xqc_h3_stream_send_data_frame(xqc_h3_stream_t *h3s, unsigned char *data, size_t h3s->data_frame.header_len = pos - h3s->data_frame.header_buf; h3s->data_frame.header_sent = 0; + fin_flag_for_quic_stream = fin; + + } else { + /* there is already a old dataframe */ + if ((data_size - data_sent) <= (h3s->data_frame.data_len - h3s->data_frame.data_sent)) { + fin_flag_for_quic_stream = fin; + + } else { + /* !!! this is important, otherwise an infinite-loop will happen. */ + fin_flag_for_quic_stream = 0; + } } /* Send frame header */ @@ -396,6 +438,8 @@ xqc_h3_stream_send_data_frame(xqc_h3_stream_t *h3s, unsigned char *data, size_t } h3s->data_frame.header_sent += sent; + xqc_log(h3s->log, XQC_LOG_DEBUG, "|send_header|success|fin:%d|fin_only:%d|\n", fin, fin_only); + if (fin_only) { if (h3s->data_frame.header_sent == h3s->data_frame.header_len) { fin_only_sent = 1; @@ -406,7 +450,7 @@ xqc_h3_stream_send_data_frame(xqc_h3_stream_t *h3s, unsigned char *data, size_t /* Send frame data */ sent = xqc_stream_send(h3s->stream, data + data_sent, - xqc_min(data_size - data_sent, h3s->data_frame.data_len - h3s->data_frame.data_sent), fin); + xqc_min(data_size - data_sent, h3s->data_frame.data_len - h3s->data_frame.data_sent), fin_flag_for_quic_stream); if (sent == -XQC_EAGAIN) { break; } else if (sent < 0) { @@ -552,6 +596,19 @@ xqc_h3_stream_send_goaway(xqc_h3_stream_t *h3s, uint64_t push_id, uint8_t fin) return XQC_OK; } +xqc_int_t +xqc_h3_stream_send_bidi_stream_type(xqc_h3_stream_t *h3s, + xqc_h3_bidi_stream_type_t stype, uint8_t fin) +{ + xqc_int_t ret = xqc_h3_stream_write_bidi_stream_type_to_buffer(h3s, stype, fin); + if (ret < 0) { + return ret; + } + + xqc_engine_main_logic_internal(h3s->h3c->conn->engine); + return XQC_OK; +} + int xqc_h3_stream_write_notify(xqc_stream_t *stream, void *user_data) @@ -580,6 +637,8 @@ xqc_h3_stream_write_notify(xqc_stream_t *stream, void *user_data) return ret; } + xqc_log(h3s->log, XQC_LOG_DEBUG, "|xqc_h3_stream_send_buffer|success|"); + /* request write */ if (h3s->type == XQC_H3_STREAM_TYPE_REQUEST && (h3s->flags & XQC_HTTP3_STREAM_NEED_WRITE_NOTIFY)) @@ -593,6 +652,20 @@ xqc_h3_stream_write_notify(xqc_stream_t *stream, void *user_data) xqc_log(h3s->log, XQC_LOG_DEBUG, "|h3_request_write_notify|success|"); } + //TODO: implement the notification of bytestream writable event + if (h3s->type == XQC_H3_STREAM_TYPE_BYTESTEAM + && (h3s->flags & XQC_HTTP3_STREAM_NEED_WRITE_NOTIFY)) + { + xqc_log(h3s->log, XQC_LOG_DEBUG, "|h3_ext_bytestream_write_notify|start|"); + ret = xqc_h3_ext_bytestream_notify_write(h3s->h3_ext_bs); + if (ret < 0) { + xqc_log(stream->stream_conn->log, XQC_LOG_ERROR, + "|h3_ext_bytestream_write_notify error|%d|", ret); + return ret; + } + xqc_log(h3s->log, XQC_LOG_DEBUG, "|h3_ext_bytestream_write_notify|success|"); + } + return XQC_OK; } @@ -935,6 +1008,97 @@ xqc_h3_stream_process_request(xqc_h3_stream_t *h3s, unsigned char *data, size_t return processed; } +ssize_t +xqc_h3_stream_process_bytestream(xqc_h3_stream_t *h3s, + unsigned char *data, size_t data_len, + xqc_bool_t fin_flag) +{ + + if (data == NULL) { + return -XQC_H3_EPARAM; + } + + xqc_h3_frame_pctx_t *pctx = &h3s->pctx.frame_pctx; + ssize_t processed = 0; + ssize_t len = 0; + xqc_int_t ret; + xqc_h3_ext_bytestream_data_buf_t *buf; + + /* process bytestream bytes */ + while (processed < data_len) { + xqc_log(h3s->log, XQC_LOG_DEBUG, "|parse frame|state:%d|data_len:%uz|process:%z|", + pctx->state, data_len, processed); + + /* parse frame, mainly the type, length field */ + ssize_t read = xqc_h3_frm_parse(data + processed, data_len - processed, pctx); + if (read < 0) { + xqc_log(h3s->log, XQC_LOG_ERROR, "|parse frame error|ret:%z|state:%d|frame_type:%xL|", + read, pctx->state, pctx->frame.type); + xqc_h3_frm_reset_pctx(pctx); + return read; + } + + xqc_log(h3s->log, XQC_LOG_DEBUG, "|parse frame success|frame_type:%xL|len:%ui|read:%z|", + pctx->frame.type, pctx->frame.len, read); + processed += read; + + xqc_bool_t fin = pctx->state == XQC_H3_FRM_STATE_END ? XQC_TRUE : XQC_FALSE; + + /* begin to parse the payload of a frame */ + if (pctx->state >= XQC_H3_FRM_STATE_PAYLOAD) { + switch (pctx->frame.type) { + case XQC_H3_FRM_HEADERS: + /* ignore header frames */ + len = xqc_min(pctx->frame.len - pctx->frame.consumed_len, data_len - processed); + processed += len; + pctx->frame.consumed_len += len; + if (pctx->frame.len == pctx->frame.consumed_len) { + fin = 1; + } + break; + + case XQC_H3_FRM_DATA: + buf = xqc_h3_ext_bytestream_get_last_data_buf(h3s->h3_ext_bs, pctx); + if (buf == NULL) { + xqc_h3_frm_reset_pctx(pctx); + return -XQC_EMALLOC; + } + + len = xqc_min(pctx->frame.len - pctx->frame.consumed_len, data_len - processed); + ret = xqc_h3_ext_bytestream_save_data_to_buf(buf, data + processed, len); + if (ret != XQC_OK) { + xqc_h3_frm_reset_pctx(pctx); + return ret; + } + + + processed += len; + pctx->frame.consumed_len += len; + + xqc_h3_ext_bytestream_recv_begin(h3s->h3_ext_bs); + + if (pctx->frame.len == pctx->frame.consumed_len) { + fin = 1; + buf->end_time = xqc_monotonic_timestamp(); + } + break; + + default: + xqc_log(h3s->log, XQC_LOG_INFO, "|ignore unexpected frame|" + "frame type:%xL|", pctx->frame.type); + break; + } + + if (fin) { + xqc_log_event(h3s->log, HTTP_FRAME_PARSED, h3s); + xqc_h3_frm_reset_pctx(pctx); + } + } + } + + return processed; +} + ssize_t xqc_h3_stream_process_uni_stream_type(const uint8_t *data, size_t data_len, @@ -954,7 +1118,7 @@ xqc_h3_stream_process_uni_stream_type(const uint8_t *data, size_t data_len, ssize_t xqc_h3_stream_process_uni_payload(xqc_h3_stream_t *h3s, unsigned char *data, size_t data_len) { - ssize_t processed; + ssize_t processed = 0; xqc_log(h3s->log, XQC_LOG_DEBUG, "|xqc_h3_stream_process_uni_payload|type:%d|sz:%uz|", h3s->type, data_len); @@ -1043,12 +1207,17 @@ ssize_t xqc_h3_stream_process_bidi_payload(xqc_h3_stream_t *h3s, unsigned char *data, size_t data_len, xqc_bool_t fin_flag) { - ssize_t processed; + ssize_t processed = 0; switch (h3s->type) { case XQC_H3_STREAM_TYPE_REQUEST: processed = xqc_h3_stream_process_request(h3s, data, data_len, fin_flag); break; + case XQC_H3_STREAM_TYPE_BYTESTEAM: + //TODO: process bytestream + processed = xqc_h3_stream_process_bytestream(h3s, data, data_len, fin_flag); + break; + /* bytes from reserved stream type will be ignored */ default: processed = data_len; @@ -1058,27 +1227,196 @@ xqc_h3_stream_process_bidi_payload(xqc_h3_stream_t *h3s, unsigned char *data, si return processed; } +xqc_int_t +xqc_h3_stream_create_inner_request_stream(xqc_h3_stream_t *h3s) +{ + if (h3s->h3r && h3s->type == XQC_H3_STREAM_TYPE_UNKNOWN) { + // if the request is already created for a stream with unknown type, + // we should return an error. + xqc_log(h3s->log, XQC_LOG_ERROR, + "|xqc_h3_request already exist in an h3 stream with unknown type|" + "stream_id:%ui|", h3s->stream_id); + return -XQC_H3_ECREATE_REQUEST; + } + + if (h3s->h3r == NULL) { + h3s->h3r = xqc_h3_request_create_inner(h3s->h3c, h3s, NULL); + if (!h3s->h3r) { + xqc_log(h3s->log, XQC_LOG_ERROR, "|xqc_h3_request_create_inner error|"); + return -XQC_H3_ECREATE_REQUEST; + } + + h3s->type = XQC_H3_STREAM_TYPE_REQUEST; + } + + return XQC_OK; +} + +xqc_int_t +xqc_h3_stream_create_inner_bytestream(xqc_h3_stream_t *h3s) +{ + if (h3s->h3_ext_bs && h3s->type == XQC_H3_STREAM_TYPE_UNKNOWN) { + // if the bytestream is already created for a stream with unknown type, + // we should return an error. + xqc_log(h3s->log, XQC_LOG_ERROR, + "|xqc_h3_ext_bytestream already exist in an h3 stream with unknown type|" + "stream_id:%ui|", h3s->stream_id); + return -XQC_H3_ECREATE_BYTESTREAM; + } + + if (h3s->h3_ext_bs == NULL) { + h3s->h3_ext_bs = xqc_h3_ext_bytestream_create_passive(h3s->h3c, h3s, NULL); + if (!h3s->h3_ext_bs) { + xqc_log(h3s->log, XQC_LOG_ERROR, "|xqc_h3_ext_bytestream_create_passive error|"); + return -XQC_H3_ECREATE_BYTESTREAM; + } + + h3s->type = XQC_H3_STREAM_TYPE_BYTESTEAM; + } + + return XQC_OK; +} + + +ssize_t +xqc_h3_stream_process_bidi_type_unknown(xqc_h3_stream_t *h3s, + unsigned char *data, size_t data_len, + xqc_bool_t fin_flag) +{ + if (data == NULL) { + return -XQC_H3_EPARAM; + } + + xqc_h3_frame_pctx_t *pctx = &h3s->pctx.frame_pctx; + ssize_t processed = 0; + xqc_int_t ret = 0; + xqc_h3_bidi_stream_type_t bidi_stream_type = 0; + + /* trying to parse the first frame */ + xqc_log(h3s->log, XQC_LOG_DEBUG, "|parse frame|state:%d|data_len:%uz|process:%z|", + pctx->state, data_len, processed); + + /* parse frame, mainly the type, length field */ + ssize_t read = xqc_h3_frm_parse(data + processed, data_len - processed, pctx); + if (read < 0) { + xqc_log(h3s->log, XQC_LOG_ERROR, "|parse frame error|ret:%z|state:%d|frame_type:%xL|", + read, pctx->state, pctx->frame.type); + xqc_h3_frm_reset_pctx(pctx); + return read; + } + + processed += read; + + xqc_bool_t fin = pctx->state == XQC_H3_FRM_STATE_END ? XQC_TRUE : XQC_FALSE; + + if (pctx->state >= XQC_H3_FRM_STATE_LEN) { + /* the type of the 1st frame is determined */ + xqc_log(h3s->log, XQC_LOG_DEBUG, "|parse frame type success|frame_type:%xL|read:%z|", + pctx->frame.type, read); + + if (pctx->frame.type != XQC_H3_EXT_FRM_BIDI_STREAM_TYPE) { + /* the first frame is not BIDI_STREAM_TYPE */ + ret = xqc_h3_stream_create_inner_request_stream(h3s); + + } else { + /* the first frame is BIDI_STREAM_TYPE, and it is parsed */ + if (fin) { + bidi_stream_type = pctx->frame.frame_payload.stream_type.stream_type.vi; + xqc_log(h3s->log, XQC_LOG_DEBUG, + "|read bidi_stream_type from BIDI_STREAM_TYPE frame|" + "bidi_stream_type:%d|", + bidi_stream_type); + switch (bidi_stream_type) { + case XQC_H3_BIDI_STREAM_TYPE_REQUEST: + ret = xqc_h3_stream_create_inner_request_stream(h3s); + break; + case XQC_H3_BIDI_STREAM_TYPE_BYTESTREAM: + //TODO: create byte stream + ret = xqc_h3_stream_create_inner_bytestream(h3s); + break; + default: + xqc_log(h3s->log, XQC_LOG_ERROR, + "|unexpected bidi stream type|" + "bidi_stream_type:%ui|", + bidi_stream_type); + ret = -XQC_H3_INVALID_BIDI_STREAM_TYPE; + break; + } + } + } + + if (ret != XQC_OK) { + xqc_h3_frm_reset_pctx(pctx); + return ret; + } + } + + if (processed == data_len && fin_flag + && h3s->type == XQC_H3_STREAM_TYPE_UNKNOWN) + { + // if we have received a fin_flag while the type of stream is still undetermined + // we treat the stream as a request stream + ret = xqc_h3_stream_create_inner_request_stream(h3s); + if (ret != XQC_OK) { + xqc_h3_frm_reset_pctx(pctx); + return ret; + } + } + + if (fin) { + xqc_log_event(h3s->log, HTTP_FRAME_PARSED, h3s); + xqc_h3_frm_reset_pctx(pctx); + } + + return processed; +} ssize_t xqc_h3_stream_process_bidi(xqc_h3_stream_t *h3s, unsigned char *data, size_t data_len, xqc_bool_t fin_flag) { + ssize_t processed = 0, total_processed = 0; + if (h3s->flags & XQC_HTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { return 0; } + xqc_log(h3s->log, XQC_LOG_DEBUG, + "|xqc_h3_stream_process_bidi|%uz|%d|", + data_len, fin_flag); + + /* + * TODO: if the stream type is unknown, + * we parse the first H3 frame to decide what type the stream is + */ + if (XQC_H3_STREAM_TYPE_UNKNOWN == h3s->type) { - h3s->type = XQC_H3_STREAM_TYPE_REQUEST; - if (!h3s->h3r) { - h3s->h3r = xqc_h3_request_create_inner(h3s->h3c, h3s, NULL); - if (!h3s->h3r) { - xqc_log(h3s->log, XQC_LOG_ERROR, "|xqc_h3_request_create_inner error|"); - return -XQC_H3_ECREATE_REQUEST; - } + if (h3s->h3c->flags & XQC_H3_CONN_FLAG_EXT_ENABLED) { + processed = xqc_h3_stream_process_bidi_type_unknown(h3s, data, data_len, fin_flag); + + } else { + /* MUST set the stream type to h3 request */ + processed = xqc_h3_stream_create_inner_request_stream(h3s); } } - return xqc_h3_stream_process_bidi_payload(h3s, data, data_len, fin_flag); + if (processed < 0) { + xqc_log(h3s->log, XQC_LOG_ERROR, + "|xqc_h3_stream_process_bidi_type_unknown error|%z|", + processed); + return processed; + } + + total_processed += processed; + + processed = xqc_h3_stream_process_bidi_payload(h3s, data + processed, data_len - processed, fin_flag); + if (processed < 0) { + return processed; + } + + total_processed += processed; + + return total_processed; } @@ -1086,8 +1424,9 @@ xqc_int_t xqc_h3_stream_process_in(xqc_h3_stream_t *h3s, unsigned char *data, size_t data_len, xqc_bool_t fin_flag) { - ssize_t processed; + ssize_t processed = 0; xqc_h3_conn_t *h3c = h3s->h3c; + xqc_int_t errcode = XQC_OK; /* nothing to process */ if (data_len == 0 && !fin_flag) { @@ -1116,14 +1455,22 @@ xqc_h3_stream_process_in(xqc_h3_stream_t *h3s, unsigned char *data, size_t data_ if (processed < 0) { /* error occurred */ xqc_log(h3c->log, XQC_LOG_ERROR, "|xqc_h3_stream_process_bidi|%z|", processed); + + errcode = -XQC_H3_EPROC_REQUEST; + + if (h3s->type == XQC_H3_STREAM_TYPE_BYTESTEAM) { + errcode = -XQC_H3_EPROC_BYTESTREAM; + } + if (processed == -XQC_H3_INVALID_HEADER) { - XQC_H3_CONN_ERR(h3c, H3_GENERAL_PROTOCOL_ERROR, -XQC_H3_EPROC_REQUEST); + XQC_H3_CONN_ERR(h3c, H3_GENERAL_PROTOCOL_ERROR, errcode); } else { - XQC_H3_CONN_ERR(h3c, H3_FRAME_ERROR, -XQC_H3_EPROC_REQUEST); + XQC_H3_CONN_ERR(h3c, H3_FRAME_ERROR, errcode); } - return -XQC_H3_EPROC_REQUEST; + // TODO: define an errorcode for bytstream? + return errcode; } else if (processed != data_len) { /* if not all bytes are processed, the decoder shall be blocked */ @@ -1285,8 +1632,15 @@ xqc_h3_stream_process_data(xqc_stream_t *stream, xqc_h3_stream_t *h3s, xqc_bool_ } while (read == buff_size && !*fin); - if (*fin && h3s->type == XQC_H3_STREAM_TYPE_REQUEST) { - h3s->h3r->fin_flag = *fin; + if (*fin) { + if (h3s->type == XQC_H3_STREAM_TYPE_REQUEST) { + h3s->h3r->fin_flag = *fin; + + } else if (h3s->type == XQC_H3_STREAM_TYPE_BYTESTEAM) { + //TODO: mark the fin flag of the bytestream and record time + xqc_h3_ext_bytestream_fin_rcvd(h3s->h3_ext_bs); + xqc_h3_ext_bytestream_set_fin_rcvd_flag(h3s->h3_ext_bs); + } } if (xqc_qpack_get_dec_insert_count(h3s->qpack) > insert_cnt) { @@ -1443,7 +1797,7 @@ xqc_h3_stream_read_notify(xqc_stream_t *stream, void *user_data) return ret; } - /* notify DATA to application ASAP */ + /* REQUEST: notify DATA to application ASAP */ if (h3s->type == XQC_H3_STREAM_TYPE_REQUEST && !xqc_list_empty(&h3s->h3r->body_buf)) { @@ -1454,6 +1808,17 @@ xqc_h3_stream_read_notify(xqc_stream_t *stream, void *user_data) return ret; } } + + /* TODO: BYTESTRAM: notify DATA to application ASAP */ + if (h3s->type == XQC_H3_STREAM_TYPE_BYTESTEAM + && xqc_h3_ext_bytestream_should_notify_read(h3s->h3_ext_bs)) + { + ret = xqc_h3_ext_bytestream_notify_read(h3s->h3_ext_bs); + if (ret < 0) { + xqc_log(h3s->log, XQC_LOG_ERROR, "|recv bytestream error|%d|", ret); + return ret; + } + } } xqc_log(h3c->log, XQC_LOG_DEBUG, "|success|stream_id:%ui|conn:%p|", @@ -1475,11 +1840,18 @@ xqc_h3_stream_close_notify(xqc_stream_t *stream, void *user_data) h3s->flags |= XQC_HTTP3_STREAM_FLAG_CLOSED; xqc_h3_stream_get_err(h3s); xqc_h3_stream_get_path_info(h3s); - if (h3s->h3r) { + + if (h3s->h3r && h3s->type == XQC_H3_STREAM_TYPE_REQUEST) { h3s->h3r->stream_fin_send_time = h3s->stream->stream_stats.local_fin_snd_time; h3s->h3r->stream_fin_ack_time = h3s->stream->stream_stats.first_fin_ack_time; h3s->h3r->stream_close_msg = h3s->stream->stream_close_msg; } + + if (h3s->h3_ext_bs && h3s->type == XQC_H3_STREAM_TYPE_BYTESTEAM) { + //TODO: record stats info + xqc_h3_ext_bytestream_save_stats_from_stream(h3s->h3_ext_bs, h3s->stream); + } + h3s->stream = NULL; /* stream closed, MUST NOT use it any more */ /* @@ -1515,9 +1887,9 @@ xqc_h3_stream_close_notify(xqc_stream_t *stream, void *user_data) * transport callback */ const xqc_stream_callbacks_t h3_stream_callbacks = { - .stream_write_notify = xqc_h3_stream_write_notify, - .stream_read_notify = xqc_h3_stream_read_notify, - .stream_close_notify = xqc_h3_stream_close_notify, + .stream_write_notify = xqc_h3_stream_write_notify, + .stream_read_notify = xqc_h3_stream_read_notify, + .stream_close_notify = xqc_h3_stream_close_notify, }; diff --git a/src/http3/xqc_h3_stream.h b/src/http3/xqc_h3_stream.h index 3589750b3..7f9df105d 100644 --- a/src/http3/xqc_h3_stream.h +++ b/src/http3/xqc_h3_stream.h @@ -22,11 +22,17 @@ typedef enum { /* bidi stream type */ XQC_H3_STREAM_TYPE_REQUEST = 0x10, + XQC_H3_STREAM_TYPE_BYTESTEAM = 0x20, /* reserved stream type or others */ XQC_H3_STREAM_TYPE_UNKNOWN = 0xFFFFFFFFFFFFFFFFull, } xqc_h3_stream_type_t; +typedef enum { + XQC_H3_BIDI_STREAM_TYPE_REQUEST = 0, + XQC_H3_BIDI_STREAM_TYPE_BYTESTREAM = 1, +} xqc_h3_bidi_stream_type_t; + typedef enum { XQC_HTTP3_STREAM_FLAG_NONE = 0x0000, XQC_HTTP3_STREAM_FLAG_TYPE_IDENTIFIED = 0x0001, @@ -92,7 +98,11 @@ typedef struct xqc_h3_stream_s { * available only in request streams, create or dereference in control or * reserved streams is forbidden. */ - xqc_h3_request_t *h3r; + union { + xqc_h3_request_t *h3r; + xqc_h3_ext_bytestream_t *h3_ext_bs; + }; + /* stream type */ xqc_h3_stream_type_t type; @@ -174,4 +184,7 @@ void xqc_h3_stream_get_path_info(xqc_h3_stream_t *h3s); void xqc_h3_stream_set_priority(xqc_h3_stream_t *h3s, xqc_h3_priority_t *prio); +xqc_int_t xqc_h3_stream_send_bidi_stream_type(xqc_h3_stream_t *h3s, + xqc_h3_bidi_stream_type_t stype, uint8_t fin); + #endif diff --git a/src/tls/xqc_tls.c b/src/tls/xqc_tls.c index 6d332ece3..3713ca90f 100644 --- a/src/tls/xqc_tls.c +++ b/src/tls/xqc_tls.c @@ -311,6 +311,21 @@ xqc_tls_create_ssl(xqc_tls_t *tls, xqc_tls_config_t *cfg) return ret; } +xqc_int_t +xqc_tls_update_tp(xqc_tls_t *tls, uint8_t *tp_buf, size_t tp_len) +{ + xqc_int_t ret = XQC_OK; + int ssl_ret; + /* set local transport parameter */ + ssl_ret = SSL_set_quic_transport_params(tls->ssl, tp_buf, tp_len); + if (ssl_ret != XQC_SSL_SUCCESS) { + xqc_log(tls->log, XQC_LOG_ERROR, "|set transport params error|%s|", + ERR_error_string(ERR_get_error(), NULL)); + ret = -XQC_TLS_INTERNAL; + } + return ret; +} + xqc_tls_t * xqc_tls_create(xqc_tls_ctx_t *ctx, xqc_tls_config_t *cfg, xqc_log_t *log, void *user_data) diff --git a/src/tls/xqc_tls.h b/src/tls/xqc_tls.h index 4c70c859e..53f9ff672 100644 --- a/src/tls/xqc_tls.h +++ b/src/tls/xqc_tls.h @@ -198,4 +198,8 @@ xqc_int_t xqc_tls_cal_retry_integrity_tag(xqc_tls_t *tls, void xqc_tls_get_selected_alpn(xqc_tls_t *tls, const char **out_alpn, size_t *out_len); +xqc_int_t xqc_tls_update_tp(xqc_tls_t *tls, uint8_t *tp_buf, size_t tp_len); + + + #endif diff --git a/src/transport/scheduler/xqc_scheduler_backup.c b/src/transport/scheduler/xqc_scheduler_backup.c index 38904ec45..9e5661b19 100644 --- a/src/transport/scheduler/xqc_scheduler_backup.c +++ b/src/transport/scheduler/xqc_scheduler_backup.c @@ -155,18 +155,9 @@ xqc_backup_probe_standby_path(xqc_connection_t *conn, if ((now - last_time) >= conn->conn_settings.standby_path_probe_timeout * 1000) { ret = xqc_path_standby_probe(path); if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_standby_probe error|ret:%d|path%ui|", ret, path->path_id); + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_standby_probe error|"); return ret; } - - xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_path_standby_probe ret:%d|" - "path:%ui|state:%d|app_path_status:%d|tra_path_status:%d|last_time:%ui|now:%ui|now-last_time:%ui|cwnd:%ui|srtt:%ui|retrans:%.4f|spurious:%.4f|\n", - ret, path->path_id, path->path_state, path->app_path_status, path->tra_path_status, - last_time, now, (now - last_time), - send_ctl->ctl_cong_callback->xqc_cong_ctl_get_cwnd(send_ctl->ctl_cong), - xqc_send_ctl_get_srtt(send_ctl), - xqc_send_ctl_get_retrans_rate(send_ctl), - xqc_send_ctl_get_spurious_loss_rate(send_ctl)); } } @@ -199,8 +190,8 @@ xqc_default_path_need_degrade(xqc_connection_t *conn, xqc_conn_get_user_data(conn)); } - - if (default_path->path_send_ctl->ctl_pto_count_since_last_tra_path_status_changed > conn->conn_settings.path_unreachable_pto_count) { + xqc_send_ctl_t *send_ctl = default_path->path_send_ctl; + if (send_ctl->ctl_pto_count_since_last_tra_path_status_changed > conn->conn_settings.path_unreachable_pto_count) { return XQC_TRUE; } @@ -243,7 +234,7 @@ xqc_probe_before_use(xqc_connection_t *conn, } else if ((now - last_send_time) >= BACKUP_PATH_PROBE_TIMEOUT) { xqc_int_t ret = xqc_path_standby_probe(next_in_use_path); if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_standby_probe error|ret:%d|path%ui|", ret, next_in_use_path->path_id); + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_standby_probe error|"); return; } } @@ -291,10 +282,7 @@ xqc_backup_scheduler_handle_conn_event(void *scheduler, } } else { - xqc_log(conn->log, XQC_LOG_ERROR, - "|path status error|default_path:%ui|app_path_status:%d|tra_path_status:%d|standby_path:%ui|app_path_status:%d|tra_path_status:%d|", - default_path->path_id, default_path->app_path_status, default_path->tra_path_status, - standby_path->path_id, standby_path->app_path_status, standby_path->tra_path_status); + xqc_log(conn->log, XQC_LOG_ERROR, "|path status error|"); return; } } diff --git a/src/transport/xqc_client.c b/src/transport/xqc_client.c index 729867924..6114e207c 100644 --- a/src/transport/xqc_client.c +++ b/src/transport/xqc_client.c @@ -14,6 +14,7 @@ #include "src/transport/xqc_utils.h" #include "src/transport/xqc_defs.h" #include "src/tls/xqc_tls.h" +#include "src/transport/xqc_datagram.h" xqc_connection_t * xqc_client_connect(xqc_engine_t *engine, const xqc_conn_settings_t *conn_settings, @@ -118,6 +119,7 @@ xqc_connect(xqc_engine_t *engine, const xqc_conn_settings_t *conn_settings, return &conn->scid_set.user_scid; } + xqc_log(engine->log, XQC_LOG_ERROR, "|xqc_client_connect error|"); return NULL; } @@ -253,11 +255,13 @@ xqc_client_create_connection(xqc_engine_t *engine, xqc_cid_t dcid, xqc_cid_t sci if (conn_ssl_config->transport_parameter_data && conn_ssl_config->transport_parameter_data_len > 0) { - xqc_memzero(&tp, sizeof(xqc_transport_params_t)); + xqc_init_transport_params(&tp); ret = xqc_read_transport_params(conn_ssl_config->transport_parameter_data, conn_ssl_config->transport_parameter_data_len, &tp); if (ret == XQC_OK) { xqc_conn_set_early_remote_transport_params(xc, &tp); + xqc_log(xc->log, XQC_LOG_DEBUG, "|0RTT_transport_params|max_datagram_frame_size:%ud|", + xc->remote_settings.max_datagram_frame_size); } } @@ -265,6 +269,8 @@ xqc_client_create_connection(xqc_engine_t *engine, xqc_cid_t dcid, xqc_cid_t sci goto fail; } + xqc_datagram_record_mss(xc); + return xc; fail: diff --git a/src/transport/xqc_conn.c b/src/transport/xqc_conn.c index d0ecb4aa5..7d273faec 100644 --- a/src/transport/xqc_conn.c +++ b/src/transport/xqc_conn.c @@ -28,6 +28,7 @@ #include "src/transport/xqc_multipath.h" #include "src/transport/xqc_reinjection.h" #include "src/tls/xqc_tls.h" +#include "src/transport/xqc_datagram.h" xqc_conn_settings_t default_conn_settings = { @@ -44,7 +45,15 @@ xqc_conn_settings_t default_conn_settings = { .anti_amplification_limit = XQC_DEFAULT_ANTI_AMPLIFICATION_LIMIT, .keyupdate_pkt_threshold = 0, .max_pkt_out_size = XQC_PACKET_OUT_SIZE, + .max_datagram_frame_size = 0, .mp_enable_reinjection = 0, + .max_ack_delay = XQC_DEFAULT_MAX_ACK_DELAY, + .ack_frequency = 2, + .loss_detection_pkt_thresh = XQC_kPacketThreshold, + .pto_backoff_factor = 2.0, + .datagram_redundancy = 0, + .datagram_force_retrans_on = 0, + .datagram_redundant_probe = 0, .reinj_flexible_deadline_srtt_factor = 2.0, .reinj_hard_deadline = XQC_MAX_UINT64_VALUE, @@ -54,6 +63,22 @@ xqc_conn_settings_t default_conn_settings = { .standby_path_probe_timeout = 0, }; + +static void +xqc_conn_dgram_probe_timeout(xqc_gp_timer_id_t gp_timer_id, + xqc_usec_t now, void *user_data) +{ + xqc_connection_t *conn = user_data; + xqc_int_t ret = XQC_OK; + size_t probe_size; + if (conn->last_dgram && conn->last_dgram->data_len != 0) { + probe_size = conn->last_dgram->data_len; + ret = xqc_datagram_send(conn, conn->last_dgram->data, probe_size, NULL, XQC_DATA_QOS_LOW); + xqc_log(conn->log, XQC_LOG_DEBUG, "|timer_based_dgram_probe|ret:%d|dgram_sz:%z|", ret, probe_size); + } +} + + void xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) { @@ -66,6 +91,20 @@ xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) default_conn_settings.sndq_packets_used_max = settings->sndq_packets_used_max; default_conn_settings.linger = settings->linger; default_conn_settings.spurious_loss_detect_on = settings->spurious_loss_detect_on; + default_conn_settings.datagram_force_retrans_on = settings->datagram_force_retrans_on; + + if (settings->max_ack_delay) { + default_conn_settings.max_ack_delay = xqc_min(settings->max_ack_delay, XQC_DEFAULT_MAX_ACK_DELAY); + } + + if (settings->datagram_redundant_probe) { + default_conn_settings.datagram_redundant_probe = xqc_max(settings->datagram_redundant_probe, + XQC_MIN_DATAGRAM_REDUNDANT_PROBE_INTERVAL); + } + + if (settings->datagram_redundancy <= XQC_MAX_DATAGRAM_REDUNDANCY) { + default_conn_settings.datagram_redundancy = settings->datagram_redundancy; + } if (settings->init_idle_time_out > 0) { default_conn_settings.init_idle_time_out = settings->init_idle_time_out; @@ -84,6 +123,7 @@ xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) } default_conn_settings.keyupdate_pkt_threshold = settings->keyupdate_pkt_threshold; + default_conn_settings.max_datagram_frame_size = settings->max_datagram_frame_size; if (settings->max_pkt_out_size > default_conn_settings.max_pkt_out_size) { default_conn_settings.max_pkt_out_size = settings->max_pkt_out_size; @@ -95,6 +135,18 @@ xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) default_conn_settings.reinj_ctl_callback = settings->reinj_ctl_callback; default_conn_settings.mp_enable_reinjection = settings->mp_enable_reinjection; + if (settings->ack_frequency > 0) { + default_conn_settings.ack_frequency = settings->ack_frequency; + } + + if (settings->pto_backoff_factor > 0) { + default_conn_settings.pto_backoff_factor = settings->pto_backoff_factor; + } + + if (settings->loss_detection_pkt_thresh > 0) { + default_conn_settings.loss_detection_pkt_thresh = settings->loss_detection_pkt_thresh; + } + if (settings->reinj_flexible_deadline_srtt_factor > 0) { default_conn_settings.reinj_flexible_deadline_srtt_factor = settings->reinj_flexible_deadline_srtt_factor; } @@ -114,6 +166,7 @@ xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) if (settings->standby_path_probe_timeout > 0) { default_conn_settings.standby_path_probe_timeout = settings->standby_path_probe_timeout; } + } static const char * const xqc_conn_flag_to_str[XQC_CONN_FLAG_SHIFT_NUM] = { @@ -152,7 +205,8 @@ static const char * const xqc_conn_flag_to_str[XQC_CONN_FLAG_SHIFT_NUM] = { [XQC_CONN_FLAG_RECV_NEW_PATH_SHIFT] = "RECV_NEW_PATH", [XQC_CONN_FLAG_VALIDATE_REBINDING_SHIFT] = "VALIDATE_REBINDING", [XQC_CONN_FLAG_CONN_CLOSING_NOTIFY_SHIFT] = "CLOSING_NOTIFY", - [XQC_CONN_FLAG_CONN_CLOSING_NOTIFIED_SHIFT] = "CLOSING_NOTIFIED" + [XQC_CONN_FLAG_CONN_CLOSING_NOTIFIED_SHIFT] = "CLOSING_NOTIFIED", + [XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT_SHIFT] = "DGRAM_WAIT_FOR_1RTT", }; unsigned char g_conn_flag_buf[1024]; @@ -240,7 +294,7 @@ xqc_conn_init_trans_settings(xqc_connection_t *conn) ls->max_data = ls->max_streams_bidi * ls->max_stream_data_bidi_local + ls->max_streams_uni * ls->max_stream_data_uni; - ls->max_idle_timeout = default_conn_settings.idle_time_out; + ls->max_idle_timeout = conn->conn_settings.idle_time_out; ls->max_udp_payload_size = XQC_CONN_MAX_UDP_PAYLOAD_SIZE; @@ -248,7 +302,10 @@ xqc_conn_init_trans_settings(xqc_connection_t *conn) ls->enable_multipath = conn->conn_settings.enable_multipath; + ls->max_datagram_frame_size = conn->conn_settings.max_datagram_frame_size; ls->disable_active_migration = ls->enable_multipath ? 0 : 1; + + ls->max_ack_delay = conn->conn_settings.max_ack_delay; } @@ -318,10 +375,36 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, xc->conn_settings = *settings; + if (xc->conn_settings.max_ack_delay == 0) { + xc->conn_settings.max_ack_delay = XQC_DEFAULT_MAX_ACK_DELAY; + } + xc->conn_settings.max_ack_delay = xqc_min(xc->conn_settings.max_ack_delay, XQC_DEFAULT_MAX_ACK_DELAY); + + if (xc->conn_settings.datagram_redundant_probe) { + xc->conn_settings.datagram_redundant_probe = xqc_max(xc->conn_settings.datagram_redundant_probe, + XQC_MIN_DATAGRAM_REDUNDANT_PROBE_INTERVAL); + } + + if (xc->conn_settings.datagram_redundancy > XQC_MAX_DATAGRAM_REDUNDANCY) { + xc->conn_settings.datagram_redundancy = XQC_MAX_DATAGRAM_REDUNDANCY; + } + if (xc->conn_settings.max_pkt_out_size < default_conn_settings.max_pkt_out_size) { xc->conn_settings.max_pkt_out_size = default_conn_settings.max_pkt_out_size; } + if (xc->conn_settings.ack_frequency == 0) { + xc->conn_settings.ack_frequency = default_conn_settings.ack_frequency; + } + + if (xc->conn_settings.pto_backoff_factor == 0) { + xc->conn_settings.pto_backoff_factor = default_conn_settings.pto_backoff_factor; + } + + if (xc->conn_settings.loss_detection_pkt_thresh == 0) { + xc->conn_settings.loss_detection_pkt_thresh = default_conn_settings.loss_detection_pkt_thresh; + } + xc->version = (type == XQC_CONN_TYPE_CLIENT) ? settings->proto_version : XQC_IDRAFT_INIT_VER; if (type == XQC_CONN_TYPE_CLIENT @@ -336,6 +419,10 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, xc->conn_settings.init_idle_time_out = XQC_CONN_INITIAL_IDLE_TIMEOUT; } + if (xc->conn_settings.idle_time_out == 0) { + xc->conn_settings.idle_time_out = XQC_CONN_DEFAULT_IDLE_TIMEOUT; + } + if (xc->conn_settings.anti_amplification_limit < XQC_DEFAULT_ANTI_AMPLIFICATION_LIMIT) { xc->conn_settings.anti_amplification_limit = XQC_DEFAULT_ANTI_AMPLIFICATION_LIMIT; } @@ -399,6 +486,7 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, xc->max_stream_id_bidi_remote = -1; xc->max_stream_id_uni_remote = -1; xc->history_path = xqc_calloc(1, sizeof(xqc_conn_path_history_t)); + xc->last_dgram = NULL; for (xqc_encrypt_level_t encrypt_level = XQC_ENC_LEV_INIT; encrypt_level < XQC_ENC_LEV_MAX; encrypt_level++) { xc->undecrypt_count[encrypt_level] = 0; @@ -418,6 +506,19 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, xqc_conn_init_timer_manager(xc); + if (xc->conn_settings.datagram_redundant_probe) { + xc->last_dgram = xqc_var_buf_create_with_limit(xqc_conn_get_mss(xc), xqc_conn_get_mss(xc)); + if (xc->last_dgram == NULL) { + goto fail; + } + + xc->dgram_probe_timer = xqc_conn_register_gp_timer(xc, "dgram_probe", xqc_conn_dgram_probe_timeout, xc); + if (xc->dgram_probe_timer < 0) { + xqc_log(xc->log, XQC_LOG_ERROR, "|register dgram probe timer error|"); + goto fail; + } + } + xqc_init_list_head(&xc->conn_write_streams); xqc_init_list_head(&xc->conn_read_streams); xqc_init_list_head(&xc->conn_closing_streams); @@ -484,6 +585,10 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, xc->pkt_filter_cb = NULL; + /* for datagram */ + xc->next_dgram_id = 0; + xqc_init_list_head(&xc->dgram_0rtt_buffer_list); + xqc_log(xc->log, XQC_LOG_DEBUG, "|success|scid:%s|dcid:%s|conn:%p|", xqc_scid_str(&xc->scid_set.user_scid), xqc_dcid_str(&xc->dcid_set.current_dcid), xc); xqc_log_event(xc->log, TRA_PARAMETERS_SET, xc, XQC_LOG_LOCAL_EVENT); @@ -666,18 +771,45 @@ xqc_conn_server_on_alpn(xqc_connection_t *conn, const unsigned char *alpn, size_ return ret; } + uint8_t tp_buf[XQC_MAX_TRANSPORT_PARAM_BUF_LEN] = {0}; + size_t tp_len = 0; + /* do callback */ if (conn->app_proto_cbs.conn_cbs.conn_create_notify) { if (conn->app_proto_cbs.conn_cbs.conn_create_notify(conn, &conn->scid_set.user_scid, conn->user_data, conn->proto_data)) { - XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); - return -TRA_INTERNAL_ERROR; + goto err; } conn->conn_flag |= XQC_CONN_FLAG_UPPER_CONN_EXIST; } + if (conn->conn_flag & XQC_CONN_FLAG_LOCAL_TP_UPDATED) { + ret = xqc_conn_encode_local_tp(conn, tp_buf, + XQC_MAX_TRANSPORT_PARAM_BUF_LEN, &tp_len); + if (ret != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, "|server encode tp error|ret:%d|", ret); + goto err; + } + + ret = xqc_tls_update_tp(conn->tls, tp_buf, tp_len); + + if (ret != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, "|server tls update tp error|ret:%d|", ret); + goto err; + } + + conn->conn_flag &= ~XQC_CONN_FLAG_LOCAL_TP_UPDATED; + xqc_log(conn->log, XQC_LOG_INFO, + "|update tp|max_datagram_frame_size:%ud|", + conn->local_settings.max_datagram_frame_size); + } + return XQC_OK; + +err: + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return -TRA_INTERNAL_ERROR; } @@ -748,6 +880,18 @@ xqc_conn_destroy(xqc_connection_t *xc) xqc_destroy_stream(stream); } + xqc_conn_destroy_0rtt_datagram_buffer_list(xc); + + if (xc->conn_settings.datagram_redundant_probe + && xc->dgram_probe_timer >= 0) { + xqc_conn_unregister_gp_timer(xc, xc->dgram_probe_timer); + } + + if (xc->last_dgram) { + xqc_var_buf_free(xc->last_dgram); + xc->last_dgram = NULL; + } + /* notify destruction */ if (xc->conn_flag & XQC_CONN_FLAG_UPPER_CONN_EXIST) { /* ALPN negotiated, notify close through application layer protocol callback function */ @@ -770,6 +914,9 @@ xqc_conn_destroy(xqc_connection_t *xc) xc->conn_flag &= ~XQC_CONN_FLAG_UPPER_CONN_EXIST; } + /* destroy gp_timer list */ + xqc_timer_destroy_gp_timer_list(&xc->conn_timer_manager); + xqc_send_queue_destroy(xc->conn_send_queue); /* free streams hash */ @@ -1103,7 +1250,8 @@ xqc_convert_pkt_0rtt_2_1rtt(xqc_connection_t *conn, xqc_packet_out_t *packet_out /* long header to short header, directly write old buffer */ unsigned int ori_po_used_size = packet_out->po_used_size; unsigned char *ori_payload = packet_out->po_payload; - unsigned int ori_payload_len = ori_po_used_size - (packet_out->po_payload - packet_out->po_buf); + unsigned int ori_hdr_len = packet_out->po_payload - packet_out->po_buf; + unsigned int ori_payload_len = ori_po_used_size - ori_hdr_len; /* convert pkt info */ packet_out->po_pkt.pkt_pns = XQC_PNS_APP_DATA; @@ -1116,11 +1264,28 @@ xqc_convert_pkt_0rtt_2_1rtt(xqc_connection_t *conn, xqc_packet_out_t *packet_out conn->key_update_ctx.cur_out_key_phase); packet_out->po_used_size = ret; + if (ori_hdr_len < ret) { + xqc_log(conn->log, XQC_LOG_ERROR, "|fatal|long_header_is_shorter_than_short_header|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return; + } + + unsigned int hdr_offset_diff = (ori_hdr_len - ret); + /* copy frame directly */ memmove(packet_out->po_buf + ret, ori_payload, ori_payload_len); packet_out->po_payload = packet_out->po_buf + ret; packet_out->po_used_size += ori_payload_len; + if (packet_out->po_ack_offset > 0) { + if (packet_out->po_ack_offset < hdr_offset_diff) { + xqc_log(conn->log, XQC_LOG_ERROR, "|fatal|wrong_ack_frame_offset|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return; + } + packet_out->po_ack_offset -= hdr_offset_diff; + } + xqc_log(conn->log, XQC_LOG_DEBUG, "|0RTT to 1RTT|conn:%p|type:%d|pkt_num:%ui|pns:%d|frame:%s|", conn, packet_out->po_pkt.pkt_type, packet_out->po_pkt.pkt_num, packet_out->po_pkt.pkt_pns, xqc_frame_type_2_str(packet_out->po_frame_types)); @@ -1306,6 +1471,11 @@ xqc_path_send_packets(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_pacing_on_packet_sent(&send_ctl->ctl_pacing, packet_out->po_used_size); } + if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + xqc_log(conn->log, XQC_LOG_DEBUG, "|dgram_id:%ui|", packet_out->po_dgram_id); + } + + /* move send list to unacked list */ xqc_path_send_buffer_remove(path, packet_out); if (XQC_IS_ACK_ELICITING(packet_out->po_frame_types)) { @@ -1427,7 +1597,7 @@ xqc_send(xqc_connection_t *conn, xqc_path_ctx_t *path, unsigned char *data, unsi if (conn->pkt_filter_cb) { sent = conn->pkt_filter_cb(data, len, (struct sockaddr *)conn->peer_addr, - conn->peer_addrlen, conn->pkt_filter_cb_user_data); + conn->peer_addrlen, conn->pkt_filter_cb_user_data); if (sent < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|pkt_filter_cb error|conn:%p|" "size:%ud|sent:%z|", conn, len, sent); @@ -1850,7 +2020,9 @@ xqc_path_send_one_or_two_ack_elicit_pkts(xqc_path_ctx_t *path, xqc_pkt_num_space } if (XQC_IS_ACK_ELICITING(packet_out->po_frame_types) - && XQC_NEED_REPAIR(packet_out->po_frame_types)) + && (XQC_NEED_REPAIR(packet_out->po_frame_types) + || (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM + && c->conn_settings.datagram_force_retrans_on))) { if (find_hsd && !(packet_out->po_frame_types & XQC_FRAME_BIT_HANDSHAKE_DONE)) { continue; @@ -1867,6 +2039,10 @@ xqc_path_send_one_or_two_ack_elicit_pkts(xqc_path_ctx_t *path, xqc_pkt_num_space xqc_send_ctl_decrease_inflight(c, packet_out); xqc_send_queue_copy_to_probe(packet_out, c->conn_send_queue, path); + if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + path->path_send_ctl->ctl_lost_dgram_cnt++; + } + if (--probe_num == 0) { break; } @@ -2170,6 +2346,31 @@ xqc_conn_send_version_negotiation(xqc_connection_t *c) return XQC_OK; } +void +xqc_conn_continue_send_by_conn(xqc_connection_t *conn) +{ + xqc_log(conn->log, XQC_LOG_DEBUG, "|conn:%p|", conn); + if (!conn) { + xqc_log(conn->engine->log, XQC_LOG_ERROR, "|can not find connection|conn:%p|", conn); + return ; + } + xqc_log(conn->log, XQC_LOG_DEBUG, "|conn:%p|", conn); + + xqc_conn_schedule_packets_to_paths(conn); + + if (xqc_engine_is_sendmmsg_on(conn->engine)) { + xqc_conn_transmit_pto_probe_packets_batch(conn); + xqc_conn_retransmit_lost_packets_batch(conn); + xqc_conn_send_packets_batch(conn); + + } else { + xqc_conn_transmit_pto_probe_packets(conn); + xqc_conn_retransmit_lost_packets(conn); + xqc_conn_send_packets(conn); + } + + xqc_engine_main_logic_internal(conn->engine); +} int xqc_conn_continue_send(xqc_engine_t *engine, const xqc_cid_t *cid) @@ -2294,6 +2495,7 @@ xqc_conn_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid) conn_stats.tlp_count += send_ctl->ctl_tlp_count; conn_stats.spurious_loss_count += send_ctl->ctl_spurious_loss_count; conn_stats.recv_count += send_ctl->ctl_recv_count; + conn_stats.lost_dgram_count += send_ctl->ctl_lost_dgram_cnt; } /* 路径信息 */ @@ -2305,6 +2507,31 @@ xqc_conn_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid) return conn_stats; } + +xqc_usec_t +xqc_conn_get_lastest_rtt(xqc_engine_t *engine, const xqc_cid_t *cid) +{ + xqc_connection_t *conn; + xqc_path_ctx_t *path; + + conn = xqc_engine_conns_hash_find(engine, cid, 's'); + if (!conn) { + xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|cid:%s", + xqc_scid_str(cid)); + return 0; + } + + path = conn->conn_initial_path; + if (!path) { + xqc_log(engine->log, XQC_LOG_ERROR, "|can not find initial path|cid:%s", + xqc_scid_str(cid)); + return 0; + } + + return path->path_send_ctl->ctl_latest_rtt; +} + + xqc_int_t xqc_conn_check_token(xqc_connection_t *conn, const unsigned char *token, unsigned token_len) { @@ -2403,6 +2630,47 @@ xqc_conn_gen_token(xqc_connection_t *conn, unsigned char *token, unsigned *token memcpy(token, &expire, sizeof(expire)); } +void +xqc_conn_resend_0rtt_datagram(xqc_connection_t *conn) +{ + xqc_list_head_t *pos, *next; + xqc_datagram_0rtt_buffer_t *dgram_buffer; + struct iovec iov[XQC_MAX_SEND_MSG_ONCE]; + uint64_t dgram_id_list[XQC_MAX_SEND_MSG_ONCE]; + size_t iov_size, sent, sent_bytes; + int ret; + + iov_size = 0; + + xqc_list_for_each_safe(pos, next, &conn->dgram_0rtt_buffer_list) { + dgram_buffer = xqc_list_entry(pos, xqc_datagram_0rtt_buffer_t, list); + iov[iov_size].iov_base = dgram_buffer->iov.iov_base; + iov[iov_size].iov_len = dgram_buffer->iov.iov_len; + dgram_id_list[iov_size] = dgram_buffer->dgram_id; + iov_size++; + if (iov_size >= XQC_MAX_SEND_MSG_ONCE) { + ret = xqc_datagram_send_multiple_internal(conn, iov, dgram_id_list, XQC_MAX_SEND_MSG_ONCE, &sent, &sent_bytes, XQC_DATA_QOS_NORMAL, XQC_TRUE, XQC_TRUE); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|unable_to_resend_0rtt_pkts_in_1rtt_way|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + iov_size = 0; + break; + } + iov_size -= XQC_MAX_SEND_MSG_ONCE; + } + } + + if (iov_size > 0) { + ret = xqc_datagram_send_multiple_internal(conn, iov, dgram_id_list, iov_size, &sent, &sent_bytes, XQC_DATA_QOS_NORMAL, XQC_TRUE, XQC_TRUE); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|unbale_to_resend_0rtt_pkts_in_1rtt_way|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + } + } + + xqc_conn_destroy_0rtt_datagram_buffer_list(conn); +} + xqc_int_t xqc_conn_early_data_reject(xqc_connection_t *conn) { @@ -2424,6 +2692,8 @@ xqc_conn_early_data_reject(xqc_connection_t *conn) xqc_send_queue_drop_0rtt_packets(conn); + xqc_conn_resend_0rtt_datagram(conn); + xqc_list_for_each_safe(pos, next, &conn->conn_all_streams) { stream = xqc_list_entry(pos, xqc_stream_t, all_stream_list); if (stream->stream_flag & XQC_STREAM_FLAG_HAS_0RTT) { @@ -2440,6 +2710,7 @@ xqc_conn_early_data_reject(xqc_connection_t *conn) xqc_stream_write_buffed_data_to_packets(stream); } } + return XQC_OK; } @@ -2460,6 +2731,9 @@ xqc_conn_early_data_accept(xqc_connection_t *conn) stream = xqc_list_entry(pos, xqc_stream_t, all_stream_list); xqc_destroy_write_buff_list(&stream->stream_write_buff_list.write_buff_list); } + + xqc_conn_destroy_0rtt_datagram_buffer_list(conn); + return XQC_OK; } @@ -2685,8 +2959,18 @@ xqc_conn_next_wakeup_time(xqc_connection_t *conn) } } - xqc_path_ctx_t *path; xqc_list_head_t *pos, *next; + xqc_gp_timer_t *gp_timer; + /* gp timer */ + xqc_list_for_each_safe(pos, next, &conn->conn_timer_manager.gp_timer_list) { + gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list); + if (gp_timer->timer_is_set) { + min_time = xqc_min(min_time, gp_timer->expire_time); + } + } + + xqc_path_ctx_t *path; + xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) { path = xqc_list_entry(pos, xqc_path_ctx_t, path_list); if (path->path_state != XQC_PATH_STATE_ACTIVE) { @@ -2885,8 +3169,10 @@ xqc_conn_confirm_cid(xqc_connection_t *c, xqc_packet_t *pkt) if (XQC_OK != xqc_cid_is_equal(&c->dcid_set.current_dcid, &pkt->pkt_scid)) { xqc_log(c->log, XQC_LOG_INFO, "|dcid change|ori:%s|new:%s|", xqc_dcid_str(&c->dcid_set.current_dcid), xqc_scid_str(&pkt->pkt_scid)); + // TODO: DCID changes xqc_cid_copy(&c->dcid_set.current_dcid, &pkt->pkt_scid); xqc_cid_copy(&c->conn_initial_path->path_dcid, &pkt->pkt_scid); + xqc_datagram_record_mss(c); } if (xqc_insert_conns_hash(c->engine->conns_hash_dcid, c, &c->dcid_set.current_dcid)) { @@ -3059,7 +3345,7 @@ xqc_conn_on_pkt_processed(xqc_connection_t *c, xqc_packet_in_t *pi, xqc_usec_t n /* record packet */ xqc_conn_record_single(c, pi); - if (pi->pi_frame_types & (~(XQC_FRAME_BIT_STREAM|XQC_FRAME_BIT_PADDING))) { + if (pi->pi_frame_types & (~(XQC_FRAME_BIT_STREAM|XQC_FRAME_BIT_DATAGRAM|XQC_FRAME_BIT_PADDING))) { c->conn_flag |= XQC_CONN_FLAG_NEED_RUN; } @@ -3155,9 +3441,11 @@ xqc_int_t xqc_conn_check_tx_key(xqc_connection_t *conn) { /* if tx key is ready, conn can send 1RTT packets */ - if (xqc_tls_is_key_ready(conn->tls, XQC_ENC_LEV_1RTT, XQC_KEY_TYPE_TX_WRITE)) { + if (!(conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT) + && xqc_tls_is_key_ready(conn->tls, XQC_ENC_LEV_1RTT, XQC_KEY_TYPE_TX_WRITE)) { xqc_log(conn->log, XQC_LOG_INFO, "|keys are ready, can send 1rtt now|"); conn->conn_flag |= XQC_CONN_FLAG_CAN_SEND_1RTT; + xqc_datagram_record_mss(conn); } return XQC_OK; @@ -3166,19 +3454,19 @@ xqc_conn_check_tx_key(xqc_connection_t *conn) xqc_int_t xqc_conn_check_handshake_complete(xqc_connection_t *conn) { + /* check tx keys after handshake complete */ + xqc_conn_check_tx_key(conn); + if (!(conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_COMPLETED) && conn->conn_state == XQC_CONN_STATE_ESTABED) { xqc_log(conn->log, XQC_LOG_DEBUG, "|HANDSHAKE_COMPLETED|conn:%p|", conn); xqc_conn_handshake_complete(conn); - if (conn->app_proto_cbs.conn_cbs.conn_handshake_finished) { conn->app_proto_cbs.conn_cbs.conn_handshake_finished(conn, conn->user_data, conn->proto_data); } } - /* check tx keys after handshake complete */ - xqc_conn_check_tx_key(conn); return XQC_OK; } @@ -3353,7 +3641,9 @@ xqc_conn_update_user_scid(xqc_connection_t *conn, xqc_scid_set_t *scid_set) xqc_conn_get_user_data(conn)); } + // TODO: SCID changes xqc_cid_copy(&scid_set->user_scid, &scid->cid); + xqc_datagram_record_mss(conn); return XQC_OK; } } @@ -3502,7 +3792,9 @@ xqc_conn_on_recv_retry(xqc_connection_t *conn, xqc_cid_t *retry_scid) conn->conn_flag |= XQC_CONN_FLAG_RETRY_RECVD; /* change the DCID it uses for sending packets in response to Retry packet. */ + // TODO: DCID changes xqc_cid_copy(&conn->dcid_set.current_dcid, retry_scid); + xqc_datagram_record_mss(conn); /* reset initial keys */ ret = xqc_tls_reset_initial(conn->tls, conn->version, retry_scid); @@ -3590,6 +3882,8 @@ xqc_conn_set_remote_transport_params(xqc_connection_t *conn, settings->active_connection_id_limit = params->active_connection_id_limit; settings->enable_multipath = params->enable_multipath; + settings->max_datagram_frame_size = params->max_datagram_frame_size; + return XQC_OK; } @@ -3624,6 +3918,7 @@ xqc_conn_get_local_transport_params(xqc_connection_t *conn, xqc_transport_params params->active_connection_id_limit = settings->active_connection_id_limit; params->no_crypto = settings->no_crypto; params->enable_multipath = settings->enable_multipath; + params->max_datagram_frame_size = settings->max_datagram_frame_size; /* set other transport parameters */ if (conn->conn_type == XQC_CONN_TYPE_SERVER @@ -3712,6 +4007,14 @@ xqc_conn_tls_transport_params_cb(const uint8_t *tp, size_t len, void *user_data) return; } + /* check datagram parameter */ + if (params.max_datagram_frame_size < conn->remote_settings.max_datagram_frame_size) { + /* 0RTT: remote_settings.max_datagram_frame_size = X */ + /* 1RTT: remote_settings.max_datagram_frame_size = 0 */ + XQC_CONN_ERR(conn, TRA_0RTT_TRANS_PARAMS_ERROR); + return; + } + /* set remote transport param */ ret = xqc_conn_set_remote_transport_params(conn, ¶ms, tp_type); if (ret != XQC_OK) { @@ -3721,6 +4024,9 @@ xqc_conn_tls_transport_params_cb(const uint8_t *tp, size_t len, void *user_data) return; } + xqc_log(conn->log, XQC_LOG_DEBUG, "|1RTT_transport_params|max_datagram_frame_size:%ud|", + conn->remote_settings.max_datagram_frame_size); + /* save no crypto flag */ if (params.no_crypto == 1) { conn->remote_settings.no_crypto = 1; @@ -3842,6 +4148,7 @@ xqc_settings_copy_from_transport_params(xqc_trans_settings_t *dest, dest->active_connection_id_limit = src->active_connection_id_limit; dest->enable_multipath = src->enable_multipath; + dest->max_datagram_frame_size = src->max_datagram_frame_size; } void @@ -3902,7 +4209,7 @@ xqc_conn_tls_keylog_cb(const char *line, void *user_data) /* invoke engine's callback */ if (eng->eng_callback.keylog_cb) { - eng->eng_callback.keylog_cb(line, eng->user_data); + eng->eng_callback.keylog_cb(&(conn->scid_set.user_scid), line, eng->user_data); } #endif } @@ -4192,7 +4499,18 @@ xqc_conn_record_histroy_path(xqc_connection_t *conn, xqc_path_ctx_t *path) xqc_int_t xqc_conn_send_path_challenge(xqc_connection_t *conn, xqc_path_ctx_t *path) { - xqc_int_t ret = XQC_OK; + xqc_int_t ret = XQC_OK; + xqc_packet_out_t *packet_out; + xqc_usec_t now; + ssize_t sent; + xqc_pn_ctl_t *pn_ctl; + + + /* send data */ + if (NULL == conn->transport_cbs.write_socket_ex) { + xqc_log(conn->log, XQC_LOG_WARN, "|write_socket_ex not registered while sending PATH_CHALLENGE"); + return XQC_ERROR; + } /* generate random data for path challenge, store it to validate path_response */ ret = xqc_generate_path_challenge_data(conn, path); @@ -4203,7 +4521,7 @@ xqc_conn_send_path_challenge(xqc_connection_t *conn, xqc_path_ctx_t *path) /* write path challenge frame & send immediately */ - xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); + packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; @@ -4226,11 +4544,10 @@ xqc_conn_send_path_challenge(xqc_connection_t *conn, xqc_path_ctx_t *path) } /* record the send time of packet */ - xqc_usec_t now = xqc_monotonic_timestamp(); + now = xqc_monotonic_timestamp(); packet_out->po_sent_time = now; - /* send data */ - ssize_t sent = conn->transport_cbs.write_socket_ex(path->path_id, conn->enc_pkt, conn->enc_pkt_len, + sent = conn->transport_cbs.write_socket_ex(path->path_id, conn->enc_pkt, conn->enc_pkt_len, (struct sockaddr *)path->rebinding_addr, path->rebinding_addrlen, xqc_conn_get_user_data(conn)); @@ -4254,7 +4571,7 @@ xqc_conn_send_path_challenge(xqc_connection_t *conn, xqc_path_ctx_t *path) xqc_log_event(conn->log, TRA_PACKET_SENT, packet_out); } - xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); + pn_ctl = xqc_get_pn_ctl(conn, path); pn_ctl->ctl_packet_number[packet_out->po_pkt.pkt_pns]++; end: @@ -4318,3 +4635,138 @@ xqc_conn_unset_pkt_filter_callback(xqc_connection_t *conn) "use write_socket again"); } } + +int +xqc_conn_buff_0rtt_datagram(xqc_connection_t *conn, void *data, size_t data_len, uint64_t dgram_id) +{ + xqc_datagram_0rtt_buffer_t *buffer = xqc_datagram_create_0rtt_buffer(data, data_len, dgram_id); + if (buffer == NULL) { + return -XQC_EMALLOC; + } + xqc_list_add_tail(&buffer->list, &conn->dgram_0rtt_buffer_list); + return XQC_OK; +} + +void +xqc_conn_destroy_0rtt_datagram_buffer_list(xqc_connection_t *conn) +{ + xqc_list_head_t *pos, *next; + xqc_datagram_0rtt_buffer_t *buffer; + xqc_list_for_each_safe(pos, next, &conn->dgram_0rtt_buffer_list) { + buffer = xqc_list_entry(pos, xqc_datagram_0rtt_buffer_t, list); + xqc_list_del_init(pos); + xqc_datagram_destroy_0rtt_buffer(buffer); + } +} + +xqc_bool_t +xqc_conn_should_clear_0rtt_ticket(xqc_int_t conn_err) +{ + if (conn_err == TRA_0RTT_TRANS_PARAMS_ERROR) { + return XQC_TRUE; + } + return XQC_FALSE; +} + +xqc_conn_settings_t +xqc_conn_get_conn_settings_template(xqc_conn_settings_type_t settings_type) +{ + xqc_conn_settings_t conn_settings = default_conn_settings; + + if (settings_type == XQC_CONN_SETTINGS_LOW_DELAY) { + conn_settings.ack_frequency = 1; + conn_settings.loss_detection_pkt_thresh = 2; + conn_settings.pto_backoff_factor = 1.5; + } + + return conn_settings; +} + +xqc_gp_timer_id_t +xqc_conn_register_gp_timer(xqc_connection_t *conn, char *timer_name, xqc_gp_timer_timeout_pt cb, void *user_data) +{ + return xqc_timer_register_gp_timer(&conn->conn_timer_manager, timer_name, cb, user_data); +} + +void +xqc_conn_unregister_gp_timer(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id) +{ + xqc_timer_unregister_gp_timer(&conn->conn_timer_manager, gp_timer_id); +} + +xqc_int_t +xqc_conn_gp_timer_set(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id, + xqc_usec_t expire_time) +{ + return xqc_timer_gp_timer_set(&conn->conn_timer_manager, gp_timer_id, expire_time); +} + +xqc_int_t +xqc_conn_gp_timer_unset(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id) +{ + return xqc_timer_gp_timer_unset(&conn->conn_timer_manager, gp_timer_id); +} + +xqc_int_t +xqc_conn_gp_timer_get_info(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id, + xqc_bool_t *is_set, xqc_usec_t *expire_time) +{ + return xqc_timer_gp_timer_get_info(&conn->conn_timer_manager, gp_timer_id, is_set, expire_time); +} + + +/** + * @brief get public local transport settings. + */ +xqc_conn_public_local_trans_settings_t +xqc_conn_get_public_local_trans_settings(xqc_connection_t *conn) +{ + xqc_conn_public_local_trans_settings_t settings; + settings.max_datagram_frame_size = conn->local_settings.max_datagram_frame_size; + settings.datagram_redundancy = conn->conn_settings.datagram_redundancy; + return settings; +} + +/** + * @brief set public local transport settings + */ +void +xqc_conn_set_public_local_trans_settings(xqc_connection_t *conn, + xqc_conn_public_local_trans_settings_t *settings) +{ + if (conn == NULL || settings == NULL) { + return; + } + + if (settings->max_datagram_frame_size != conn->local_settings.max_datagram_frame_size) { + conn->local_settings.max_datagram_frame_size = settings->max_datagram_frame_size; + conn->conn_settings.max_datagram_frame_size = settings->max_datagram_frame_size; + conn->conn_flag |= XQC_CONN_FLAG_LOCAL_TP_UPDATED; + } + + conn->conn_settings.datagram_redundancy = settings->datagram_redundancy; + if (conn->conn_settings.datagram_redundancy > XQC_MAX_DATAGRAM_REDUNDANCY) { + conn->conn_settings.datagram_redundancy = XQC_MAX_DATAGRAM_REDUNDANCY; + } +} + +/** + * @brief get public remote transport settings. + */ +xqc_conn_public_remote_trans_settings_t +xqc_conn_get_public_remote_trans_settings(xqc_connection_t *conn) +{ + xqc_conn_public_remote_trans_settings_t settings; + settings.max_datagram_frame_size = conn->remote_settings.max_datagram_frame_size; + return settings; +} + +/** + * @brief set public remote transport settings + */ +void +xqc_conn_set_public_remote_trans_settings(xqc_connection_t *conn, + xqc_conn_public_remote_trans_settings_t *settings) +{ + conn->remote_settings.max_datagram_frame_size = settings->max_datagram_frame_size; +} \ No newline at end of file diff --git a/src/transport/xqc_conn.h b/src/transport/xqc_conn.h index 5a53132cd..6742d99e2 100644 --- a/src/transport/xqc_conn.h +++ b/src/transport/xqc_conn.h @@ -20,7 +20,10 @@ #include "src/transport/xqc_timer.h" #include "src/transport/xqc_multipath.h" #include "src/tls/xqc_tls.h" +#include "src/common/xqc_list.h" +#define XQC_MAX_DATAGRAM_REDUNDANCY 2 +#define XQC_MIN_DATAGRAM_REDUNDANT_PROBE_INTERVAL 30000 /* 30ms min probing interval */ #define XQC_TOKEN_EXPIRE_DELTA (7 * 24 * 60 * 60) /* expire in N seconds */ #define XQC_TOKEN_UPDATE_DELTA (XQC_TOKEN_EXPIRE_DELTA / 2) /* early update */ @@ -125,6 +128,8 @@ typedef enum { XQC_CONN_FLAG_VALIDATE_REBINDING_SHIFT, XQC_CONN_FLAG_CONN_CLOSING_NOTIFY_SHIFT, XQC_CONN_FLAG_CONN_CLOSING_NOTIFIED_SHIFT, + XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT_SHIFT, + XQC_CONN_FLAG_LOCAL_TP_UPDATED_SHIFT, XQC_CONN_FLAG_SHIFT_NUM, } xqc_conn_flag_shift_t; @@ -165,6 +170,8 @@ typedef enum { XQC_CONN_FLAG_VALIDATE_REBINDING = 1ULL << XQC_CONN_FLAG_VALIDATE_REBINDING_SHIFT, XQC_CONN_FLAG_CLOSING_NOTIFY = 1ULL << XQC_CONN_FLAG_CONN_CLOSING_NOTIFY_SHIFT, XQC_CONN_FLAG_CLOSING_NOTIFIED = 1ULL << XQC_CONN_FLAG_CONN_CLOSING_NOTIFIED_SHIFT, + XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT = 1ULL << XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT_SHIFT, + XQC_CONN_FLAG_LOCAL_TP_UPDATED = 1ULL << XQC_CONN_FLAG_LOCAL_TP_UPDATED_SHIFT, } xqc_conn_flag_t; @@ -187,6 +194,7 @@ typedef struct { uint64_t active_connection_id_limit; uint64_t no_crypto; uint64_t enable_multipath; + uint16_t max_datagram_frame_size; } xqc_trans_settings_t; @@ -299,6 +307,8 @@ struct xqc_connection_s { xqc_app_proto_callbacks_t app_proto_cbs; void *proto_data; + void *dgram_data; + xqc_list_head_t undecrypt_packet_in[XQC_ENC_LEV_MAX]; /* buffer for reordered packets */ uint32_t undecrypt_count[XQC_ENC_LEV_MAX]; @@ -361,6 +371,13 @@ struct xqc_connection_s { xqc_conn_pkt_filter_callback_pt pkt_filter_cb; void *pkt_filter_cb_user_data; + /* for datagram */ + uint64_t next_dgram_id; + xqc_list_head_t dgram_0rtt_buffer_list; + uint16_t dgram_mss; + + xqc_gp_timer_id_t dgram_probe_timer; + xqc_var_buf_t *last_dgram; /* history path */ xqc_conn_path_history_t *history_path; }; @@ -534,6 +551,22 @@ void xqc_conn_record_histroy_path(xqc_connection_t *conn, xqc_path_ctx_t *path); xqc_int_t xqc_conn_send_path_challenge(xqc_connection_t *conn, xqc_path_ctx_t *path); +int xqc_conn_buff_0rtt_datagram(xqc_connection_t *conn, void *data, size_t data_len, uint64_t dgram_id); + +void xqc_conn_destroy_0rtt_datagram_buffer_list(xqc_connection_t *conn); +void xqc_conn_resend_0rtt_datagram(xqc_connection_t *conn); + +xqc_gp_timer_id_t xqc_conn_register_gp_timer(xqc_connection_t *conn, char *timer_name, xqc_gp_timer_timeout_pt cb, void *user_data); + +void xqc_conn_unregister_gp_timer(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id); + +xqc_int_t xqc_conn_gp_timer_set(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id, xqc_usec_t expire_time); + +xqc_int_t xqc_conn_gp_timer_unset(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id); + +xqc_int_t xqc_conn_gp_timer_get_info(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id, xqc_bool_t *is_set, xqc_usec_t *expire_time); + + void xqc_conn_schedule_packets_to_paths(xqc_connection_t *conn); static inline xqc_uint_t diff --git a/src/transport/xqc_datagram.c b/src/transport/xqc_datagram.c new file mode 100644 index 000000000..fd8c37ad5 --- /dev/null +++ b/src/transport/xqc_datagram.c @@ -0,0 +1,543 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#include "src/transport/xqc_datagram.h" +#include "src/transport/xqc_conn.h" +#include "src/transport/xqc_packet_parser.h" +#include "src/common/xqc_log.h" +#include "src/transport/xqc_send_ctl.h" +#include "src/transport/xqc_utils.h" +#include "src/transport/xqc_engine.h" + + +xqc_datagram_0rtt_buffer_t* +xqc_datagram_create_0rtt_buffer(void *data, size_t data_len, uint64_t dgram_id) +{ + xqc_datagram_0rtt_buffer_t *buffer = xqc_malloc(sizeof(xqc_datagram_0rtt_buffer_t)); + if (buffer == NULL) { + return NULL; + } + + buffer->iov.iov_base = NULL; + + if (data_len > 0) { + buffer->iov.iov_base = xqc_malloc(data_len); + if (buffer->iov.iov_base == NULL) { + xqc_free(buffer); + return NULL; + } + xqc_memcpy(buffer->iov.iov_base, data, data_len); + } + + buffer->iov.iov_len = data_len; + buffer->dgram_id = dgram_id; + xqc_init_list_head(&buffer->list); + return buffer; +} + +void +xqc_datagram_destroy_0rtt_buffer(xqc_datagram_0rtt_buffer_t* buffer) +{ + if (buffer) { + if (buffer->iov.iov_base) { + xqc_free(buffer->iov.iov_base); + } + xqc_free(buffer); + } +} + +void +xqc_datagram_record_mss(xqc_connection_t *conn) +{ + size_t udp_payload_limit = 0, dgram_frame_limit = 0, mtu_limit = 0; + size_t quic_header_size, headroom; + + if (conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT) { + quic_header_size = xqc_short_packet_header_size(conn->dcid_set.current_dcid.cid_len, XQC_PKTNO_BITS); + + } else { + if (conn->conn_type == XQC_CONN_TYPE_CLIENT) { + quic_header_size = xqc_long_packet_header_size(conn->dcid_set.current_dcid.cid_len, conn->scid_set.user_scid.cid_len, 0, XQC_PKTNO_BITS, XQC_PTYPE_0RTT); + + } else { + conn->dgram_mss = 0; + return; + } + } + + headroom = XQC_ACK_SPACE + XQC_TLS_AEAD_OVERHEAD_MAX_LEN + quic_header_size + XQC_DATAGRAM_HEADER_BYTES; + if (conn->remote_settings.max_udp_payload_size >= headroom) { + udp_payload_limit = conn->remote_settings.max_udp_payload_size - headroom; + + } else { + udp_payload_limit = 0; + } + + headroom = quic_header_size + XQC_DATAGRAM_HEADER_BYTES; + if (conn->conn_settings.max_pkt_out_size >= headroom) { + mtu_limit = conn->conn_settings.max_pkt_out_size - headroom; + + } else { + mtu_limit = 0; + } + + dgram_frame_limit = conn->remote_settings.max_datagram_frame_size >= XQC_DATAGRAM_HEADER_BYTES ? + conn->remote_settings.max_datagram_frame_size - XQC_DATAGRAM_HEADER_BYTES : + 0; + + conn->dgram_mss = xqc_min(xqc_min(dgram_frame_limit, udp_payload_limit), mtu_limit); +} + + +/* + * @brief the API to get the max length of the data that can be sent + * via a single call of xqc_datagram_send + * + * @param conn the connection handle + * @return 0 = the peer does not support datagram, >0 = the max length + */ +size_t +xqc_datagram_get_mss(xqc_connection_t *conn) +{ + return conn->dgram_mss; +} + +/* + * @brief the API to send a datagram over the QUIC connection + * + * @param conn the connection handle + * @param data the data to be sent + * @param data_len the length of the data + * @param *dgram_id the pointer to return the id the datagram + * @return <0 = error (-XQC_EAGAIN, -XQC_CLOSING, -XQC_EDGRAM_TOO_LARGE, ...), + * 0 success + */ +xqc_int_t xqc_datagram_send(xqc_connection_t *conn, void *data, + size_t data_len, uint64_t *dgram_id, xqc_data_qos_level_t qos_level) +{ + if (conn == NULL) { + return -XQC_EPARAM; + } + + if (data == NULL && data_len != 0) { + return -XQC_EPARAM; + } + + if (conn->conn_state >= XQC_CONN_STATE_CLOSING) { + xqc_conn_log(conn, XQC_LOG_INFO, "|conn closing, cannot send datagram|size:%ud|", data_len); + return -XQC_CLOSING; + } + + if (conn->remote_settings.max_datagram_frame_size == 0) { + if (conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT) { + xqc_conn_log(conn, XQC_LOG_INFO, "|does not support datagram|size:%ud|", data_len); + return -XQC_EDGRAM_NOT_SUPPORTED; + } else { + /*may receive max_datagram_frame_size later */ + xqc_log(conn->log, XQC_LOG_DEBUG, "|waiting_for_max_datagram_frame_size_from_peer|"); + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + return -XQC_EAGAIN; + } + } + + if (conn->dgram_mss < data_len) { + xqc_log(conn->log, XQC_LOG_INFO, "|datagram_is_too_large|data_len:%ud|", + data_len); + return -XQC_EDGRAM_TOO_LARGE; + } + + /* max_datagram_frame_size > 0 */ + + int ret; + xqc_pkt_type_t pkt_type = XQC_PTYPE_SHORT_HEADER; + int support_0rtt = xqc_conn_is_ready_to_send_early_data(conn); + uint64_t dg_id; + + if (!(conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT)) { + if ((conn->conn_type == XQC_CONN_TYPE_CLIENT) + && (conn->conn_state == XQC_CONN_STATE_CLIENT_INITIAL_SENT) + && support_0rtt) + { + pkt_type = XQC_PTYPE_0RTT; + conn->conn_flag |= XQC_CONN_FLAG_HAS_0RTT; + + } else { + xqc_log(conn->log, XQC_LOG_DEBUG, "|does_not_support_0rtt_when_sending_datagram|"); + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + return -XQC_EAGAIN; + } + } + + if (!xqc_send_queue_can_write(conn->conn_send_queue)) { + conn->conn_send_queue->sndq_full = XQC_TRUE; + xqc_log(conn->log, XQC_LOG_DEBUG, "|too many packets used|ctl_packets_used:%ud|", conn->conn_send_queue->sndq_packets_used); + return -XQC_EAGAIN; + } + + if (pkt_type == XQC_PTYPE_0RTT && conn->zero_rtt_count >= XQC_PACKET_0RTT_MAX_COUNT) { + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + xqc_log(conn->log, XQC_LOG_DEBUG, "|too many 0rtt packets|zero_rtt_count:%ud|", conn->zero_rtt_count); + return -XQC_EAGAIN; + } + + xqc_conn_check_app_limit(conn); + + ret = xqc_write_datagram_frame_to_packet(conn, pkt_type, data, data_len, &dg_id, XQC_FALSE); + + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|write_datagram_frame_to_packet_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + + /* 0RTT failure requires fallback to 1RTT, save the original send data */ + if (pkt_type == XQC_PTYPE_0RTT) { + /* buffer 0RTT packet */ + ret = xqc_conn_buff_0rtt_datagram(conn, data, data_len, dg_id); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|unable_to_buffer_0rtt_datagram_data_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + } + + if (dgram_id) { + *dgram_id = dg_id; + } + + if (qos_level <= XQC_DATA_QOS_HIGH) { + int red; + for (red = 0; red < conn->conn_settings.datagram_redundancy; red++) { + if (!xqc_send_queue_can_write(conn->conn_send_queue)) { + conn->conn_send_queue->sndq_full = XQC_TRUE; + xqc_log(conn->log, XQC_LOG_DEBUG, "|red_dgram|too many packets used|ctl_packets_used:%ud|", conn->conn_send_queue->sndq_packets_used); + break; + } + + if (pkt_type == XQC_PTYPE_0RTT && conn->zero_rtt_count >= XQC_PACKET_0RTT_MAX_COUNT) { + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + xqc_log(conn->log, XQC_LOG_DEBUG, "|red_dgram|too many 0rtt packets|zero_rtt_count:%ud|", conn->zero_rtt_count); + break; + } + + ret = xqc_write_datagram_frame_to_packet(conn, pkt_type, data, data_len, &dg_id, XQC_FALSE); + + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|red_dgram|write_datagram_frame_to_packet_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + + /* 0RTT failure requires fallback to 1RTT, save the original send data */ + if (pkt_type == XQC_PTYPE_0RTT) { + /* buffer 0RTT packet */ + ret = xqc_conn_buff_0rtt_datagram(conn, data, data_len, dg_id); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|red_dgram|unable_to_buffer_0rtt_datagram_data_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + } + } + } + + if (!(conn->conn_flag & XQC_CONN_FLAG_TICKING)) { + if (0 == xqc_conns_pq_push(conn->engine->conns_active_pq, conn, conn->last_ticked_time)) { + conn->conn_flag |= XQC_CONN_FLAG_TICKING; + } + } + + if (conn->conn_settings.datagram_redundant_probe + && conn->dgram_probe_timer >= 0 + && qos_level <= XQC_DATA_QOS_NORMAL + && conn->last_dgram + && data_len <= conn->last_dgram->buf_len) + { + xqc_var_buf_clear(conn->last_dgram); + xqc_var_buf_save_data(conn->last_dgram, data, data_len); + xqc_conn_gp_timer_set(conn, conn->dgram_probe_timer, xqc_monotonic_timestamp() + conn->conn_settings.datagram_redundant_probe); + xqc_log(conn->log, XQC_LOG_DEBUG, "|start_dgram_probe_timer|data_len:%z|", data_len); + } + + /* call main logic to send packets out */ + xqc_engine_main_logic_internal(conn->engine); + + return XQC_OK; +} + +/* + * @brief the API to send a datagram over the QUIC connection + * + * @param conn the connection handle + * @param iov multiple data buffers need to be sent + * @param *dgram_id the pointer to return the list of dgram_id + * @param iov_size the size of iov list + * @param *sent_cnt the number of successfully sent datagrams + * @param *sent_bytes the total bytes of successfully sent datagrams + * @return <0 = error (-XQC_EAGAIN, -XQC_CLOSING, -XQC_EDGRAM_NOT_SUPPORTED, -XQC_EDGRAM_TOO_LARGE, ...), + * 0 success + */ +xqc_int_t +xqc_datagram_send_multiple(xqc_connection_t *conn, + struct iovec *iov, uint64_t *dgram_id_list, size_t iov_size, + size_t *sent_cnt, size_t *sent_bytes, xqc_data_qos_level_t qos_level) +{ + return xqc_datagram_send_multiple_internal(conn, iov, dgram_id_list, iov_size, sent_cnt, sent_bytes, qos_level, XQC_FALSE, XQC_FALSE); +} + +xqc_int_t +xqc_datagram_send_multiple_internal(xqc_connection_t *conn, + struct iovec *iov, uint64_t *dgram_id_list, size_t iov_size, + size_t *sent_cnt, size_t *sent_bytes, xqc_data_qos_level_t qos_level, + xqc_bool_t use_supplied_dgram_id, xqc_bool_t disable_redundancy) +{ + if (sent_cnt) { + *sent_cnt = 0; + } + + if (sent_bytes) { + *sent_bytes = 0; + } + + if (conn == NULL || iov == NULL || iov_size == 0 || sent_cnt == NULL || sent_bytes == NULL) { + return -XQC_EPARAM; + } + + if (use_supplied_dgram_id && dgram_id_list == NULL) { + return -XQC_EPARAM; + } + + if (conn->conn_state >= XQC_CONN_STATE_CLOSING) { + xqc_conn_log(conn, XQC_LOG_INFO, "|conn closing, cannot send datagram|"); + return -XQC_CLOSING; + } + + if (conn->remote_settings.max_datagram_frame_size == 0) { + if (conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT) { + xqc_conn_log(conn, XQC_LOG_INFO, "|does not support datagram|"); + return -XQC_EDGRAM_NOT_SUPPORTED; + } else { + /*may receive max_datagram_frame_size later */ + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + xqc_log(conn->log, XQC_LOG_DEBUG, "|waiting_for_max_datagram_frame_size_from_peer|"); + return -XQC_EAGAIN; + } + } + + int ret = XQC_OK; + xqc_pkt_type_t pkt_type = XQC_PTYPE_SHORT_HEADER; + int support_0rtt = xqc_conn_is_ready_to_send_early_data(conn); + + if (!(conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT)) { + if ((conn->conn_type == XQC_CONN_TYPE_CLIENT) + && (conn->conn_state == XQC_CONN_STATE_CLIENT_INITIAL_SENT) + && support_0rtt) + { + pkt_type = XQC_PTYPE_0RTT; + conn->conn_flag |= XQC_CONN_FLAG_HAS_0RTT; + + } else { + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + xqc_log(conn->log, XQC_LOG_DEBUG, "|does_not_support_0rtt_when_sending_datagram|"); + return -XQC_EAGAIN; + } + } + + int i; + void *data; + size_t data_len; + uint8_t check_applimit = 1; + uint64_t dgram_id; + + for (i = 0; i < iov_size; *sent_cnt = ++i) { + data = iov[i].iov_base; + data_len = iov[i].iov_len; + + if (data == NULL && data_len != 0) { + ret = -XQC_EPARAM; + break; + } + + if (conn->dgram_mss < data_len) { + xqc_log(conn->log, XQC_LOG_INFO, "|datagram_is_too_large|data_len:%ud|", + data_len); + return -XQC_EDGRAM_TOO_LARGE; + } + + if (!xqc_send_queue_can_write(conn->conn_send_queue)) { + conn->conn_send_queue->sndq_full = XQC_TRUE; + xqc_log(conn->log, XQC_LOG_DEBUG, "|too many packets used|ctl_packets_used:%ud|", conn->conn_send_queue->sndq_packets_used); + ret = -XQC_EAGAIN; + break; + } + + if (pkt_type == XQC_PTYPE_0RTT && conn->zero_rtt_count >= XQC_PACKET_0RTT_MAX_COUNT) { + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + xqc_log(conn->log, XQC_LOG_DEBUG, "|too many 0rtt packets|zero_rtt_count:%ud|", conn->zero_rtt_count); + ret = -XQC_EAGAIN; + break; + } + + if (check_applimit) { + xqc_conn_check_app_limit(conn); + check_applimit = 0; + } + + if (use_supplied_dgram_id) { + dgram_id = dgram_id_list[i]; + } + + ret = xqc_write_datagram_frame_to_packet(conn, pkt_type, data, data_len, &dgram_id, use_supplied_dgram_id); + + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|write_datagram_frame_to_packet_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + + /* 0RTT failure requires fallback to 1RTT, save the original send data */ + if (pkt_type == XQC_PTYPE_0RTT) { + /* buffer 0RTT packet */ + ret = xqc_conn_buff_0rtt_datagram(conn, data, data_len, dgram_id); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|unable_to_buffer_0rtt_datagram_data_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + } + + if (dgram_id_list) { + dgram_id_list[i] = dgram_id; + } + + *sent_bytes += data_len; + } + + /* send redundant datagram packets */ + if (disable_redundancy == XQC_FALSE + && (*sent_cnt > 0) + && (qos_level <= XQC_DATA_QOS_HIGH)) + { + int red; + for (red = 0; red < conn->conn_settings.datagram_redundancy; red++) { + for (i = 0; i < iov_size; i++) { + data = iov[i].iov_base; + data_len = iov[i].iov_len; + + if (!xqc_send_queue_can_write(conn->conn_send_queue)) { + conn->conn_send_queue->sndq_full = XQC_TRUE; + xqc_log(conn->log, XQC_LOG_DEBUG, "|red_dgram|too many packets used|ctl_packets_used:%ud|", conn->conn_send_queue->sndq_packets_used); + break; + } + + if (pkt_type == XQC_PTYPE_0RTT && conn->zero_rtt_count >= XQC_PACKET_0RTT_MAX_COUNT) { + conn->conn_flag |= XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + xqc_log(conn->log, XQC_LOG_DEBUG, "|red_dgram|too many 0rtt packets|zero_rtt_count:%ud|", conn->zero_rtt_count); + break; + } + + ret = xqc_write_datagram_frame_to_packet(conn, pkt_type, data, data_len, &dgram_id, XQC_FALSE); + + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|red_dgram|write_datagram_frame_to_packet_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + + /* 0RTT failure requires fallback to 1RTT, save the original send data */ + if (pkt_type == XQC_PTYPE_0RTT) { + /* buffer 0RTT packet */ + ret = xqc_conn_buff_0rtt_datagram(conn, data, data_len, dgram_id); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|red_dgram|unable_to_buffer_0rtt_datagram_data_error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); + return ret; + } + } + } + } + } + + if (*sent_cnt > 0) { + if (!(conn->conn_flag & XQC_CONN_FLAG_TICKING)) { + if (0 == xqc_conns_pq_push(conn->engine->conns_active_pq, conn, conn->last_ticked_time)) { + conn->conn_flag |= XQC_CONN_FLAG_TICKING; + } + } + + data = iov[*sent_cnt - 1].iov_base; + data_len = iov[*sent_cnt - 1].iov_len; + + if (conn->conn_settings.datagram_redundant_probe + && conn->dgram_probe_timer >= 0 + && qos_level <= XQC_DATA_QOS_NORMAL + && conn->last_dgram + && data_len <= conn->last_dgram->buf_len) + { + xqc_var_buf_clear(conn->last_dgram); + xqc_var_buf_save_data(conn->last_dgram, data, data_len); + xqc_conn_gp_timer_set(conn, conn->dgram_probe_timer, xqc_monotonic_timestamp() + conn->conn_settings.datagram_redundant_probe); + xqc_log(conn->log, XQC_LOG_DEBUG, "|start_dgram_probe_timer|data_len:%z|", data_len); + } + + /* call main logic to send packets out */ + xqc_engine_main_logic_internal(conn->engine); + } + + return ret < 0 ? ret : XQC_OK; +} +void +xqc_datagram_set_user_data(xqc_connection_t *conn, void *dgram_data) +{ + conn->dgram_data = dgram_data; +} + +void* +xqc_datagram_get_user_data(xqc_connection_t *conn) +{ + return conn->dgram_data; +} + +void +xqc_datagram_notify_write(xqc_connection_t *conn) +{ + if (conn->remote_settings.max_datagram_frame_size > 0 + && (conn->conn_flag & XQC_CONN_FLAG_UPPER_CONN_EXIST)) + { + if (conn->app_proto_cbs.dgram_cbs.datagram_write_notify) { + conn->app_proto_cbs.dgram_cbs.datagram_write_notify(conn, conn->dgram_data); + } + } +} + +xqc_int_t +xqc_datagram_notify_loss(xqc_connection_t *conn, xqc_packet_out_t *po) +{ + if (conn->app_proto_cbs.dgram_cbs.datagram_lost_notify + && (conn->conn_flag & XQC_CONN_FLAG_UPPER_CONN_EXIST)) + { + return conn->app_proto_cbs.dgram_cbs.datagram_lost_notify(conn, po->po_dgram_id, conn->dgram_data); + } + return 0; +} + +void +xqc_datagram_notify_ack(xqc_connection_t *conn, xqc_packet_out_t *po) +{ + /* if it is already acked, do not notify */ + if (po->po_acked + || (po->po_origin && po->po_origin->po_acked)) + { + xqc_log(conn->log, XQC_LOG_DEBUG, + "|datagram already notified|dgram_id:%ui|", po->po_dgram_id); + return; + } + if (conn->app_proto_cbs.dgram_cbs.datagram_acked_notify + && (conn->conn_flag & XQC_CONN_FLAG_UPPER_CONN_EXIST)) + { + xqc_log(conn->log, XQC_LOG_DEBUG, + "|notify datagram acked to app|dgram_id:%ui|", po->po_dgram_id); + conn->app_proto_cbs.dgram_cbs.datagram_acked_notify(conn, po->po_dgram_id, conn->dgram_data); + } +} \ No newline at end of file diff --git a/src/transport/xqc_datagram.h b/src/transport/xqc_datagram.h new file mode 100644 index 000000000..d78ccbe44 --- /dev/null +++ b/src/transport/xqc_datagram.h @@ -0,0 +1,37 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#ifndef _XQC_DATAGRAM_H_INCLUDED_ +#define _XQC_DATAGRAM_H_INCLUDED_ + +#include +#include +#include "src/common/xqc_list.h" +#include "src/transport/xqc_conn.h" +#include "src/transport/xqc_frame_parser.h" + +typedef struct xqc_datagram_0rtt_buffer_s { + xqc_list_head_t list; + struct iovec iov; + uint64_t dgram_id; +} xqc_datagram_0rtt_buffer_t; + +xqc_datagram_0rtt_buffer_t* xqc_datagram_create_0rtt_buffer(void *data, size_t data_len, uint64_t dgram_id); + +void xqc_datagram_destroy_0rtt_buffer(xqc_datagram_0rtt_buffer_t* buffer); + +void xqc_datagram_record_mss(xqc_connection_t *conn); + +void xqc_datagram_notify_write(xqc_connection_t *conn); + +xqc_int_t xqc_datagram_notify_loss(xqc_connection_t *conn, xqc_packet_out_t *po); + +void xqc_datagram_notify_ack(xqc_connection_t *conn, xqc_packet_out_t *po); + +xqc_int_t xqc_datagram_send_multiple_internal(xqc_connection_t *conn, + struct iovec *iov, uint64_t *dgram_id_list, size_t iov_size, + size_t *sent_cnt, size_t *sent_bytes, xqc_data_qos_level_t qos_level, + xqc_bool_t use_supplied_dgram_id, xqc_bool_t disable_redundancy); + +#endif \ No newline at end of file diff --git a/src/transport/xqc_engine.c b/src/transport/xqc_engine.c index b4029227b..5e91d2e9b 100644 --- a/src/transport/xqc_engine.c +++ b/src/transport/xqc_engine.c @@ -21,8 +21,10 @@ #include "src/transport/xqc_wakeup_pq.h" #include "src/transport/xqc_utils.h" #include "src/transport/xqc_timer.h" +#include "src/transport/xqc_datagram.h" #include "src/http3/xqc_h3_conn.h" #include "src/tls/xqc_tls.h" +#include "src/transport/xqc_datagram.h" extern const xqc_qpack_ins_cb_t xqc_h3_qpack_ins_cb; @@ -44,6 +46,7 @@ xqc_config_t default_client_config = { .reset_token_key = {0}, .reset_token_keylen = 0, .sendmmsg_on = 0, + .enable_h3_ext = 0, }; @@ -64,6 +67,7 @@ xqc_config_t default_server_config = { .reset_token_key = {0}, .reset_token_keylen = 0, .sendmmsg_on = 0, + .enable_h3_ext = 0, }; @@ -125,6 +129,7 @@ xqc_set_config(xqc_config_t *dst, const xqc_config_t *src) dst->cfg_log_timestamp = src->cfg_log_timestamp; dst->cfg_log_level_name = src->cfg_log_level_name; dst->sendmmsg_on = src->sendmmsg_on; + dst->enable_h3_ext = src->enable_h3_ext; return XQC_OK; } @@ -692,7 +697,21 @@ xqc_engine_process_conn(xqc_connection_t *conn, xqc_usec_t now) if (conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT) { xqc_process_read_streams(conn); if (xqc_send_queue_can_write(conn->conn_send_queue)) { - xqc_process_write_streams(conn); + if (conn->conn_send_queue->sndq_full) { + if (xqc_send_queue_release_enough_space(conn->conn_send_queue)) { + conn->conn_send_queue->sndq_full = XQC_FALSE; + xqc_process_write_streams(conn); + xqc_datagram_notify_write(conn); + } + + } else { + xqc_process_write_streams(conn); + if (conn->conn_flag & XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT) { + xqc_datagram_notify_write(conn); + conn->conn_flag &= ~XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT; + } + } + } else { xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_send_queue_can_write false|"); } @@ -785,7 +804,7 @@ xqc_engine_main_logic(xqc_engine_t *engine) } engine->eng_flag |= XQC_ENG_FLAG_RUNNING; - xqc_log(engine->log, XQC_LOG_DEBUG, "|"); + xqc_log(engine->log, XQC_LOG_DEBUG, "|BEGIN|"); xqc_usec_t now = xqc_monotonic_timestamp(); xqc_connection_t *conn; @@ -926,6 +945,8 @@ xqc_engine_main_logic(xqc_engine_t *engine) } engine->eng_flag &= ~XQC_ENG_FLAG_RUNNING; + + xqc_log(engine->log, XQC_LOG_DEBUG, "|END|"); return; } diff --git a/src/transport/xqc_frame.c b/src/transport/xqc_frame.c index b5cf53a18..a289d5f45 100644 --- a/src/transport/xqc_frame.c +++ b/src/transport/xqc_frame.c @@ -45,6 +45,7 @@ static const char * const frame_type_2_str[XQC_FRAME_NUM] = { [XQC_FRAME_ACK_MP] = "ACK_MP", [XQC_FRAME_PATH_ABANDON] = "PATH_ABANDON", [XQC_FRAME_PATH_STATUS] = "PATH_STATUS", + [XQC_FRAME_DATAGRAM] = "DATAGRAM", [XQC_FRAME_Extension] = "Extension", }; @@ -264,6 +265,9 @@ xqc_process_frames(xqc_connection_t *conn, xqc_packet_in_t *packet_in) case 0x1e: ret = xqc_process_handshake_done_frame(conn, packet_in); break; + case 0x30 ... 0x31: + ret = xqc_process_datagram_frame(conn, packet_in); + break; case 0xbaba00 ... 0xbaba01: ret = xqc_process_ack_mp_frame(conn, packet_in); break; @@ -371,8 +375,9 @@ xqc_process_stream_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) } if (stream_frame->fin) { - if (stream->stream_data_in.stream_length > 0 - && stream->stream_data_in.stream_length != stream_frame->data_offset + stream_frame->data_length) { + if (stream->stream_data_in.stream_determined + && stream->stream_data_in.stream_length != stream_frame->data_offset + stream_frame->data_length) + { xqc_log(conn->log, XQC_LOG_ERROR, "|final size changed|stream_id:%ui|", stream_id); XQC_CONN_ERR(conn, TRA_FINAL_SIZE_ERROR); ret = -XQC_EPROTO; @@ -384,13 +389,14 @@ xqc_process_stream_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) } stream->stream_data_in.stream_length = stream_frame->data_offset + stream_frame->data_length; + stream->stream_data_in.stream_determined = XQC_TRUE; if (stream->stream_state_recv == XQC_RECV_STREAM_ST_RECV) { xqc_stream_recv_state_update(stream, XQC_RECV_STREAM_ST_SIZE_KNOWN); } } - if (stream->stream_data_in.stream_length > 0 + if (stream->stream_data_in.stream_determined && stream_frame->data_offset + stream_frame->data_length > stream->stream_data_in.stream_length) { xqc_log(conn->log, XQC_LOG_ERROR, "|exceed final size|stream_id:%ui|", stream_id); @@ -431,8 +437,8 @@ xqc_process_stream_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) return -XQC_EPROTO; } - if (stream->stream_data_in.stream_length == stream->stream_data_in.merged_offset_end - && stream->stream_data_in.stream_length > 0) + if (stream->stream_data_in.stream_determined + && stream->stream_data_in.stream_length == stream->stream_data_in.merged_offset_end) { if (stream->stream_state_recv == XQC_RECV_STREAM_ST_SIZE_KNOWN) { xqc_stream_recv_state_update(stream, XQC_RECV_STREAM_ST_DATA_RECVD); @@ -1039,7 +1045,8 @@ xqc_process_streams_blocked_frame(xqc_connection_t *conn, xqc_packet_in_t *packe return XQC_OK; } - new_max_streams = stream_limit + conn->local_settings.max_streams_bidi; + new_max_streams = xqc_min(stream_limit + conn->local_settings.max_streams_bidi, + conn->conn_flow_ctl.fc_max_streams_bidi_can_recv + conn->local_settings.max_streams_bidi); conn->conn_flow_ctl.fc_max_streams_bidi_can_recv = new_max_streams; } else { @@ -1048,7 +1055,8 @@ xqc_process_streams_blocked_frame(xqc_connection_t *conn, xqc_packet_in_t *packe return XQC_OK; } - new_max_streams = stream_limit + conn->local_settings.max_streams_uni; + new_max_streams = xqc_min(stream_limit + conn->local_settings.max_streams_uni, + conn->conn_flow_ctl.fc_max_streams_uni_can_recv + conn->local_settings.max_streams_uni); conn->conn_flow_ctl.fc_max_streams_uni_can_recv = new_max_streams; } @@ -1196,6 +1204,49 @@ xqc_process_new_token_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) return XQC_OK; } +xqc_int_t +xqc_process_datagram_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) +{ + /* does not support datagram */ + if (conn->local_settings.max_datagram_frame_size == 0) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|the endpoint does not support datagram but receives a DATAGRAM frame|"); + XQC_CONN_ERR(conn, TRA_PROTOCOL_VIOLATION); + return -XQC_EPROTO; + } + + unsigned char *data_buffer = NULL; + size_t data_len = 0; + + xqc_int_t ret = xqc_parse_datagram_frame(packet_in, conn, &data_buffer, &data_len); + if (ret == -XQC_EPROTO) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|the endpoint receives a DATAGRAM frame larger than max_datagram_frame_size|" + "max_datagram_frame_size:%ud|frame_size:%ud|", + conn->local_settings.max_datagram_frame_size, + data_len + XQC_DATAGRAM_HEADER_BYTES); + XQC_CONN_ERR(conn, TRA_PROTOCOL_VIOLATION); + return ret; + } + if (ret != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_parse_datagram_frame error|"); + return ret; + } + + /* @TODO: datagram read callback */ + if (data_len > 0) { + if (conn->app_proto_cbs.dgram_cbs.datagram_read_notify + && (conn->conn_flag & XQC_CONN_FLAG_UPPER_CONN_EXIST)) + { + conn->app_proto_cbs.dgram_cbs.datagram_read_notify(conn, conn->dgram_data, data_buffer, data_len, xqc_monotonic_timestamp() - packet_in->pkt_recv_time); + xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_datagram_read|data_len:%z|", data_len); + } + } + + return ret; +} + + xqc_int_t xqc_process_handshake_done_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) @@ -1480,10 +1531,6 @@ xqc_process_path_status_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in if (path_status_seq_num > path->app_path_status_recv_seq_num) { path->app_path_status_recv_seq_num = path_status_seq_num; xqc_set_application_path_status(path, path_status, now); - - xqc_log(conn->log, XQC_LOG_DEBUG, - "|path:%ui|path_id_type:%ui|path_id_content:%ui|app_path_status:%d|seq_num:%ui|tra_path_status:%d|", - path->path_id, path_id_type, path_id_content, path->app_path_status, path_status_seq_num, path->tra_path_status); } return XQC_OK; diff --git a/src/transport/xqc_frame.h b/src/transport/xqc_frame.h index b6bf1901f..a4ef5736f 100644 --- a/src/transport/xqc_frame.h +++ b/src/transport/xqc_frame.h @@ -31,6 +31,7 @@ typedef enum { XQC_FRAME_ACK_MP, XQC_FRAME_PATH_ABANDON, XQC_FRAME_PATH_STATUS, + XQC_FRAME_DATAGRAM, XQC_FRAME_Extension, XQC_FRAME_NUM, } xqc_frame_type_t; @@ -59,6 +60,7 @@ typedef enum { XQC_FRAME_BIT_ACK_MP = 1 << XQC_FRAME_ACK_MP, XQC_FRAME_BIT_PATH_ABANDON = 1 << XQC_FRAME_PATH_ABANDON, XQC_FRAME_BIT_PATH_STATUS = 1 << XQC_FRAME_PATH_STATUS, + XQC_FRAME_BIT_DATAGRAM = 1 << XQC_FRAME_DATAGRAM, XQC_FRAME_BIT_Extension = 1 << XQC_FRAME_Extension, XQC_FRAME_BIT_NUM = 1 << XQC_FRAME_NUM, } xqc_frame_type_bit_t; @@ -91,7 +93,7 @@ typedef enum { * PING and PADDING frames contain no information, so lost PING or * PADDING frames do not require repair */ -#define XQC_NEED_REPAIR(types) ((types) & ~(XQC_FRAME_BIT_ACK| XQC_FRAME_BIT_PADDING | XQC_FRAME_BIT_PING | XQC_FRAME_BIT_CONNECTION_CLOSE)) +#define XQC_NEED_REPAIR(types) ((types) & ~(XQC_FRAME_BIT_ACK| XQC_FRAME_BIT_PADDING | XQC_FRAME_BIT_PING | XQC_FRAME_BIT_CONNECTION_CLOSE | XQC_FRAME_BIT_DATAGRAM)) const char *xqc_frame_type_2_str(xqc_frame_type_bit_t type_bit); @@ -150,4 +152,6 @@ xqc_int_t xqc_process_path_abandon_frame(xqc_connection_t *conn, xqc_packet_in_t xqc_int_t xqc_process_path_status_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in); +xqc_int_t xqc_process_datagram_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in); + #endif /* _XQC_FRAME_H_INCLUDED_ */ diff --git a/src/transport/xqc_frame_parser.c b/src/transport/xqc_frame_parser.c index 01b5d58b8..99ac96f0e 100644 --- a/src/transport/xqc_frame_parser.c +++ b/src/transport/xqc_frame_parser.c @@ -14,6 +14,88 @@ #include "src/transport/xqc_packet_parser.h" #include "src/transport/xqc_reinjection.h" +/** + * generate datagram frame + */ +xqc_int_t xqc_gen_datagram_frame(xqc_packet_out_t *packet_out, + const unsigned char *payload, size_t size) +{ + if (packet_out == NULL) { + return -XQC_EPARAM; + } + + unsigned char *dst_buf = packet_out->po_buf + packet_out->po_used_size; + size_t dst_buf_len = packet_out->po_buf_size - packet_out->po_used_size; + unsigned char *p = dst_buf + 1; + + if ((size + 1 + XQC_DATAGRAM_LENGTH_FIELD_BYTES) > dst_buf_len) { + return -XQC_ENOBUF; + } + + xqc_vint_write(p, size, XQC_DATAGRAM_LENGTH_FIELD_BYTES - 1, XQC_DATAGRAM_LENGTH_FIELD_BYTES); + p += XQC_DATAGRAM_LENGTH_FIELD_BYTES; + + if (size > 0) { + xqc_memcpy(p, payload, size); + } + + p += size; + + dst_buf[0] = 0x31; + + packet_out->po_frame_types |= XQC_FRAME_BIT_DATAGRAM; + packet_out->po_used_size += p - dst_buf; + + return XQC_OK; +} + +xqc_int_t xqc_parse_datagram_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, + unsigned char **buffer, size_t *size) +{ + uint64_t length; + int vlen = 0; + const unsigned char *p = packet_in->pos; + const unsigned char *end = packet_in->last; + /* skip frame type */ + const unsigned char first_byte = *p++; + xqc_bool_t has_length = (first_byte & 0x1); + + if (has_length) { + /*currently, the length of length field is 2 bytes*/ + vlen = xqc_vint_read(p, end, &length); + if (vlen < 0) { + return -XQC_EVINTREAD; + } + + p += vlen; + + if (length > (end - p)) { + return -XQC_EILLEGAL_FRAME; + } + + } else { + length = end - p; + } + + /* recv a DATAGRAM frame larger than max_datagram_frame_size */ + if ((length + 1 + vlen) > conn->local_settings.max_datagram_frame_size) { + return -XQC_EPROTO; + } + + *size = length; + *buffer = (unsigned char *)p; + + p += length; + + packet_in->pos = (unsigned char *)p; + packet_in->pi_frame_types |= XQC_FRAME_BIT_DATAGRAM; + + xqc_log_event(conn->log, TRA_FRAMES_PROCESSED, XQC_FRAME_DATAGRAM, length); + + return XQC_OK; +} + + ssize_t @@ -230,10 +312,10 @@ xqc_parse_stream_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, return -XQC_EVINTREAD; } + p += vlen; if (length > end - p) { return -XQC_EILLEGAL_FRAME; } - p += vlen; frame->data_length = length; } else { @@ -280,7 +362,7 @@ xqc_parse_stream_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, */ ssize_t xqc_gen_crypto_frame(xqc_packet_out_t *packet_out, uint64_t offset, - const unsigned char *payload, size_t payload_size, size_t *written_size) + const unsigned char *payload, uint64_t payload_size, size_t *written_size) { unsigned char *dst_buf = packet_out->po_buf + packet_out->po_used_size; size_t dst_buf_len = packet_out->po_buf_size - packet_out->po_used_size; @@ -1727,6 +1809,9 @@ xqc_parse_path_challenge_frame(xqc_packet_in_t *packet_in, unsigned char *data) const unsigned char *end = packet_in->last; const unsigned char first_byte = *p++; + if (p + XQC_PATH_CHALLENGE_DATA_LEN > end) { + return -XQC_EVINTREAD; + } xqc_memcpy(data, p, XQC_PATH_CHALLENGE_DATA_LEN); p += XQC_PATH_CHALLENGE_DATA_LEN; @@ -1784,6 +1869,10 @@ xqc_parse_path_response_frame(xqc_packet_in_t *packet_in, unsigned char *data) const unsigned char *end = packet_in->last; const unsigned char first_byte = *p++; + if (p + XQC_PATH_CHALLENGE_DATA_LEN > end) { + return -XQC_EVINTREAD; + } + xqc_memcpy(data, p, XQC_PATH_CHALLENGE_DATA_LEN); p += XQC_PATH_CHALLENGE_DATA_LEN; @@ -2316,4 +2405,4 @@ xqc_parse_path_status_frame(xqc_packet_in_t *packet_in, packet_in->pi_frame_types |= XQC_FRAME_BIT_PATH_STATUS; return XQC_OK; -} \ No newline at end of file +} diff --git a/src/transport/xqc_frame_parser.h b/src/transport/xqc_frame_parser.h index f960a1c25..6a026c3e6 100644 --- a/src/transport/xqc_frame_parser.h +++ b/src/transport/xqc_frame_parser.h @@ -12,7 +12,17 @@ #include "src/transport/xqc_recv_record.h" #define XQC_PATH_CHALLENGE_DATA_LEN 8 +#define XQC_DATAGRAM_LENGTH_FIELD_BYTES 2 +#define XQC_DATAGRAM_HEADER_BYTES (XQC_DATAGRAM_LENGTH_FIELD_BYTES + 1) +/** + * generate datagram frame + */ +xqc_int_t xqc_gen_datagram_frame(xqc_packet_out_t *packet_out, + const unsigned char *payload, size_t size); + +xqc_int_t xqc_parse_datagram_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, + unsigned char **buffer, size_t *size); /** * generate stream frame @@ -27,7 +37,7 @@ xqc_int_t xqc_parse_stream_frame(xqc_packet_in_t *packet_in, xqc_connection_t *c xqc_stream_frame_t *frame, xqc_stream_id_t *stream_id); ssize_t xqc_gen_crypto_frame(xqc_packet_out_t *packet_out, uint64_t offset, - const unsigned char *payload, size_t payload_size, size_t *written_size); + const unsigned char *payload, uint64_t payload_size, size_t *written_size); xqc_int_t xqc_parse_crypto_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, xqc_stream_frame_t * frame); diff --git a/src/transport/xqc_multipath.c b/src/transport/xqc_multipath.c index 90abbef19..9d827b2e7 100644 --- a/src/transport/xqc_multipath.c +++ b/src/transport/xqc_multipath.c @@ -12,6 +12,8 @@ #include "src/transport/xqc_wakeup_pq.h" #include "src/transport/xqc_packet_out.h" #include "src/transport/xqc_reinjection.h" +#include "src/transport/xqc_frame_parser.h" +#include "src/transport/xqc_datagram.h" #include "src/common/xqc_common.h" #include "src/common/xqc_malloc.h" @@ -197,8 +199,8 @@ xqc_path_init(xqc_path_ctx_t *path, xqc_connection_t *conn) xqc_set_path_state(path, XQC_PATH_STATE_VALIDATING); } - xqc_log(conn->engine->log, XQC_LOG_DEBUG, "|path:%ui|dcid:%s|scid:%s|state:%d|app_path_status:%d|tra_path_status:%d|", - path->path_id, xqc_dcid_str(&path->path_dcid), xqc_scid_str(&path->path_scid), path->path_state, path->app_path_status, path->tra_path_status); + xqc_log(conn->engine->log, XQC_LOG_DEBUG, "|path:%ui|dcid:%s|scid:%s|state:%d|", + path->path_id, xqc_dcid_str(&path->path_dcid), xqc_scid_str(&path->path_scid), path->path_state); return XQC_OK; } @@ -212,9 +214,11 @@ xqc_path_move_unack_packets_from_conn(xqc_path_ctx_t *path, xqc_connection_t *co xqc_list_head_t *pos, *next; xqc_packet_out_t *po = NULL; uint64_t closing_path_id = path->path_id; + xqc_int_t repair_dgram = 0; xqc_list_for_each_safe(pos, next, &conn->conn_send_queue->sndq_unacked_packets[XQC_PNS_APP_DATA]) { po = xqc_list_entry(pos, xqc_packet_out_t, po_list); + repair_dgram = 0; if (xqc_send_ctl_indirectly_ack_po(conn, po)) { continue; @@ -223,7 +227,31 @@ xqc_path_move_unack_packets_from_conn(xqc_path_ctx_t *path, xqc_connection_t *co if (po->po_path_id == closing_path_id) { if (po->po_flag & XQC_POF_IN_FLIGHT) { xqc_send_ctl_decrease_inflight(conn, po); - xqc_send_queue_copy_to_lost(po, conn->conn_send_queue); + + if (po->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + path->path_send_ctl->ctl_lost_dgram_cnt++; + repair_dgram = xqc_datagram_notify_loss(conn, po); + if (conn->conn_settings.datagram_force_retrans_on) { + repair_dgram = XQC_DGRAM_RETX_ASKED_BY_APP; + } + } + + if (XQC_NEED_REPAIR(po->po_frame_types) + || repair_dgram == XQC_DGRAM_RETX_ASKED_BY_APP) + { + xqc_send_queue_copy_to_lost(po, conn->conn_send_queue); + + } else { + /* for datagram, we should remove all copies in the unacked list */ + if (po->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + xqc_send_ctl_may_remove_unacked_dgram(conn, po); + + } else { + /* if a packet don't need to be repair, don't retransmit it */ + xqc_send_queue_remove_unacked(po, conn->conn_send_queue); + xqc_send_queue_insert_free(po, &conn->conn_send_queue->sndq_free_packets, conn->conn_send_queue); + } + } } } } @@ -440,6 +468,14 @@ xqc_conn_close_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t closed return ret; } + if (!(conn->conn_flag & XQC_CONN_FLAG_TICKING)) { + if (0 == xqc_conns_pq_push(conn->engine->conns_active_pq, conn, conn->last_ticked_time)) { + conn->conn_flag |= XQC_CONN_FLAG_TICKING; + } + } + + xqc_engine_main_logic_internal(engine); + return XQC_OK; } @@ -595,36 +631,66 @@ xqc_request_path_metrics_print(xqc_connection_t *conn, xqc_h3_stream_t *h3_strea stats->mp_default_path_send_weight = 1.0; stats->mp_default_path_recv_weight = 1.0; + stats->mp_standby_path_send_weight = 0.0; + stats->mp_standby_path_recv_weight = 0.0; + if (!conn->enable_multipath) { return; } - if (conn->validated_path_count < 2) { + if (conn->active_path_count < 2) { return; } - int aggregate_cnt = 0; - uint64_t all_path_send_bytes = 0, all_path_recv_bytes = 0; + int available_path_cnt = 0, standby_path_cnt = 0; - for (int i = 0; i < XQC_MAX_PATHS_COUNT; ++i) { - if (h3_stream->paths_info[i].path_pkt_recv_count > 0 - || h3_stream->paths_info[i].path_pkt_send_count > 0) - { - aggregate_cnt++; + uint64_t aggregate_send_bytes = 0, aggregate_recv_bytes = 0; + uint64_t standby_path_send_bytes = 0, standby_path_recv_bytes = 0; - all_path_send_bytes += h3_stream->paths_info[i].path_send_bytes; - all_path_recv_bytes += h3_stream->paths_info[i].path_recv_bytes; + xqc_list_head_t *pos, *next; + xqc_path_ctx_t *path; + xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) { + path = xqc_list_entry(pos, xqc_path_ctx_t, path_list); + + if (path->path_id < XQC_MAX_PATHS_COUNT + && path->path_id == h3_stream->paths_info[path->path_id].path_id) + { + uint64_t send_bytes = h3_stream->paths_info[path->path_id].path_send_bytes; + uint64_t recv_bytes = h3_stream->paths_info[path->path_id].path_recv_bytes; + + if (send_bytes > 0 || recv_bytes > 0) { + aggregate_send_bytes += send_bytes; + aggregate_recv_bytes += recv_bytes; + + if (path->app_path_status == XQC_APP_PATH_STATUS_STANDBY) { + standby_path_cnt++; + standby_path_send_bytes += send_bytes; + standby_path_recv_bytes += recv_bytes; + } else { + available_path_cnt++; + } + } } } - stats->mp_state = (aggregate_cnt > 1) ? 1 : 2; + if ((available_path_cnt > 0) && (standby_path_cnt > 0)) { + stats->mp_state = 1; + + } else if ((available_path_cnt == 0) && (standby_path_cnt > 0)) { + stats->mp_state = 2; - if (all_path_send_bytes != 0) { - stats->mp_default_path_send_weight = (float)(h3_stream->paths_info[XQC_INITIAL_PATH_ID].path_send_bytes) / all_path_send_bytes; + } else if ((available_path_cnt > 0) && (standby_path_cnt == 0)) { + stats->mp_state = 3; } - if (all_path_recv_bytes != 0) { - stats->mp_default_path_recv_weight = (float)(h3_stream->paths_info[XQC_INITIAL_PATH_ID].path_recv_bytes) / all_path_recv_bytes; + if (aggregate_send_bytes != 0) { + stats->mp_default_path_send_weight = (float)(h3_stream->paths_info[XQC_INITIAL_PATH_ID].path_send_bytes) / aggregate_send_bytes; + stats->mp_standby_path_send_weight = (float)(standby_path_send_bytes) / aggregate_send_bytes; + } + + if (aggregate_recv_bytes != 0) { + stats->mp_default_path_recv_weight = (float)(h3_stream->paths_info[XQC_INITIAL_PATH_ID].path_recv_bytes) / aggregate_recv_bytes; + stats->mp_standby_path_recv_weight = (float)(standby_path_recv_bytes) / aggregate_recv_bytes; } } @@ -1030,7 +1096,7 @@ xqc_path_validate(xqc_path_ctx_t *path) xqc_int_t ret = xqc_write_path_status_frame_to_packet(conn, path); if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_path_status_frame_to_packet error|%d|", ret); + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_path_status_frame_to_packet error|"); } } } @@ -1307,13 +1373,10 @@ xqc_path_standby_probe(xqc_path_ctx_t *path) xqc_int_t ret = xqc_path_send_ping_to_probe(path, XQC_PNS_APP_DATA); if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_send_ping_to_probe error|path:%ui|ret:%d|", path->path_id, ret); return ret; } path->standby_probe_count++; - xqc_log(conn->log, XQC_LOG_DEBUG, "|conn:%p|path:%ui|PING|conn_state:%s|", conn, path->path_id, xqc_conn_state_2_str(conn->conn_state)); - return XQC_OK; } diff --git a/src/transport/xqc_multipath.h b/src/transport/xqc_multipath.h index 4db5e1931..a222e61c0 100644 --- a/src/transport/xqc_multipath.h +++ b/src/transport/xqc_multipath.h @@ -31,6 +31,7 @@ typedef enum { XQC_PATH_STATE_DRAINING = 4, /* PATH_ABANDONED received */ XQC_PATH_STATE_CLOSED = 5, /* PATH_ABANDONED acked or draining timeout */ } xqc_path_state_t; + /* application layer path status */ typedef enum { /* suggest that no traffic should be sent on that path if another path is available */ @@ -38,6 +39,7 @@ typedef enum { /* allow the peer to use its own logic to split traffic among available paths */ XQC_APP_PATH_STATUS_AVAILABLE = 2, } xqc_app_path_status_t; + /* transport layer path status */ typedef enum { XQC_TRA_PATH_STATUS_BACKUP = 1, diff --git a/src/transport/xqc_pacing.c b/src/transport/xqc_pacing.c index b15f25128..0d34f06bd 100644 --- a/src/transport/xqc_pacing.c +++ b/src/transport/xqc_pacing.c @@ -141,7 +141,7 @@ xqc_pacing_can_write(xqc_pacing_t *pacing, uint32_t total_bytes) } uint64_t delay = xqc_pacing_time_until_send(pacing, total_bytes); - xqc_log(send_ctl->ctl_conn->log, XQC_LOG_DEBUG, "|pacing_delay: %ui!", delay); + xqc_log(send_ctl->ctl_conn->log, XQC_LOG_DEBUG, "|pacing_delay: %ui|", delay); if (delay != 0) { xqc_timer_update(&send_ctl->path_timer_manager, XQC_TIMER_PACING, xqc_monotonic_timestamp(), delay); diff --git a/src/transport/xqc_packet_out.c b/src/transport/xqc_packet_out.c index a4df30406..add95ddb3 100644 --- a/src/transport/xqc_packet_out.c +++ b/src/transport/xqc_packet_out.c @@ -14,6 +14,7 @@ #include "src/transport/xqc_utils.h" #include "src/transport/xqc_engine.h" #include "src/transport/xqc_multipath.h" +#include "src/transport/xqc_datagram.h" xqc_packet_out_t * @@ -391,7 +392,9 @@ xqc_write_ack_to_packets(xqc_connection_t *conn) path_buffer: xqc_list_for_each_safe(pos, next, &conn->conn_initial_path->path_schedule_buf[XQC_SEND_TYPE_NORMAL]) { packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); - if (packet_out->po_pkt.pkt_type == pkt_type) { + if (packet_out->po_pkt.pkt_type == pkt_type + && !(packet_out->po_frame_types & XQC_FRAME_BIT_ACK)) + { ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); if (ret == -XQC_ENOBUF) { xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_to_one_packet try conn buffer|"); @@ -410,7 +413,10 @@ xqc_write_ack_to_packets(xqc_connection_t *conn) conn_buffer: xqc_list_for_each_safe(pos, next, &conn->conn_send_queue->sndq_send_packets) { packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); - if (packet_out->po_pkt.pkt_type == pkt_type && !packet_out->po_is_path_specified) { + if (packet_out->po_pkt.pkt_type == pkt_type + && !packet_out->po_is_path_specified + && !(packet_out->po_frame_types & XQC_FRAME_BIT_ACK)) + { ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); if (ret == -XQC_ENOBUF) { xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_to_one_packet try new packet|"); @@ -867,6 +873,42 @@ xqc_write_stream_frame_to_packet(xqc_connection_t *conn, return XQC_OK; } +int +xqc_write_datagram_frame_to_packet(xqc_connection_t *conn, xqc_pkt_type_t pkt_type, + const unsigned char *data, size_t data_len, uint64_t *dgram_id, xqc_bool_t use_supplied_dgram_id) +{ + xqc_packet_out_t *packet_out; + packet_out = xqc_write_new_packet(conn, pkt_type); + if (packet_out == NULL) { + return -XQC_EWRITE_PKT; + } + + int ret; + ret = xqc_gen_datagram_frame(packet_out, data, data_len); + + if (ret < 0) { + xqc_maybe_recycle_packet_out(packet_out, conn); + return ret; + } + + if (use_supplied_dgram_id) { + packet_out->po_dgram_id = *dgram_id; + + } else { + packet_out->po_dgram_id = conn->next_dgram_id++; + } + + if (dgram_id) { + *dgram_id = packet_out->po_dgram_id; + } + + if (pkt_type == XQC_PTYPE_0RTT) { + conn->zero_rtt_count++; + } + + return XQC_OK; +} + /* [Transport] 12.4, HANDSHAKE_DONE only send in 1-RTT packet */ int @@ -957,12 +999,14 @@ xqc_write_retire_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t seq_nu xqc_int_t ret = XQC_ERROR; /* select new current_dcid to replace the cid to be retired */ - if (seq_num == conn->dcid_set.current_dcid.cid_seq_num) { + if (seq_num == conn->dcid_set.current_dcid.cid_seq_num) { + // TODO: DCID changes ret = xqc_get_unused_cid(&conn->dcid_set.cid_set, &conn->dcid_set.current_dcid); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|conn don't have available dcid|"); return ret; - } + } + xqc_datagram_record_mss(conn); } xqc_log(conn->log, XQC_LOG_DEBUG, "|get_new_dcid:%s|seq_num:%ui|", xqc_dcid_str(&conn->dcid_set.current_dcid), conn->dcid_set.current_dcid.cid_seq_num); @@ -1103,7 +1147,8 @@ xqc_write_ack_mp_to_packets(xqc_connection_t *conn) path_buffer: xqc_list_for_each_safe(pos, next, &path->path_schedule_buf[XQC_SEND_TYPE_NORMAL]) { packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); - if (packet_out->po_pkt.pkt_type == pkt_type) { + if (packet_out->po_pkt.pkt_type == pkt_type + && !(packet_out->po_frame_types & XQC_FRAME_BIT_ACK_MP)) { ret = xqc_write_ack_mp_to_one_packet(conn, path, packet_out, pns); if (ret == -XQC_ENOBUF) { xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_to_one_packet try new packet|"); @@ -1244,12 +1289,8 @@ xqc_write_path_status_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *pa } packet_out->po_used_size += ret; - xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); - xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|path_status_seq_num:%ui|app_path_status:%d|tra_path_status:%d|", - path->path_id, path->app_path_status_send_seq_num, path->app_path_status, path->tra_path_status); - return XQC_OK; error: diff --git a/src/transport/xqc_packet_out.h b/src/transport/xqc_packet_out.h index ea9e75cd3..157497f02 100644 --- a/src/transport/xqc_packet_out.h +++ b/src/transport/xqc_packet_out.h @@ -37,6 +37,7 @@ typedef enum { XQC_POF_IN_UNACK_LIST = 1 << 12, /* FIXED: reset when copy */ XQC_POF_NOT_SCHEDULE = 1 << 13, XQC_POF_NOT_REINJECT = 1 << 14, + XQC_POF_DROPPED_DGRAM = 1 << 15, } xqc_packet_out_flag_t; typedef struct xqc_po_stream_frame_s { @@ -89,6 +90,9 @@ typedef struct xqc_packet_out_s { /* how many packets have been lost when the packet is sent */ uint32_t po_lost; + /* only meaningful if it contains a DATAGRAM frame */ + uint64_t po_dgram_id; + /* Multipath */ xqc_bool_t po_is_path_specified; uint64_t po_path_id; @@ -152,6 +156,9 @@ int xqc_write_new_token_to_packet(xqc_connection_t *conn); int xqc_write_stream_frame_to_packet(xqc_connection_t *conn, xqc_stream_t *stream, xqc_pkt_type_t pkt_type, uint8_t fin, const unsigned char *payload, size_t payload_size, size_t *send_data_written); +int xqc_write_datagram_frame_to_packet(xqc_connection_t *conn, xqc_pkt_type_t pkt_type, + const unsigned char *data, size_t data_len, uint64_t *dgram_id, xqc_bool_t use_supplied_dgram_id); + int xqc_write_handshake_done_frame_to_packet(xqc_connection_t *conn); xqc_int_t xqc_write_new_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t retire_prior_to); diff --git a/src/transport/xqc_recv_record.c b/src/transport/xqc_recv_record.c index 39111cca1..1a8bb1efb 100644 --- a/src/transport/xqc_recv_record.c +++ b/src/transport/xqc_recv_record.c @@ -283,7 +283,7 @@ xqc_maybe_should_ack(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_pn_ctl_t xqc_send_ctl_t *send_ctl = path->path_send_ctl; - if (send_ctl->ctl_ack_eliciting_pkt[pns] >= 2 + if (send_ctl->ctl_ack_eliciting_pkt[pns] >= conn->conn_settings.ack_frequency || (pns <= XQC_PNS_HSK && send_ctl->ctl_ack_eliciting_pkt[pns] >= 1) || (out_of_order && send_ctl->ctl_ack_eliciting_pkt[pns] >= 1)) { @@ -293,8 +293,11 @@ xqc_maybe_should_ack(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_pn_ctl_t xqc_timer_unset(&send_ctl->path_timer_manager, XQC_TIMER_ACK_INIT + pns); xqc_log(conn->log, XQC_LOG_DEBUG, "|yes|path:%ui|out_of_order:%d|ack_eliciting_pkt:%ud|" - "pns:%d|flag:%s|", path->path_id, out_of_order, send_ctl->ctl_ack_eliciting_pkt[pns], - pns, xqc_conn_flag_2_str(conn->conn_flag)); + "pns:%d|flag:%s|ack_freq:%ud|", + path->path_id, out_of_order, + send_ctl->ctl_ack_eliciting_pkt[pns], + pns, xqc_conn_flag_2_str(conn->conn_flag), + conn->conn_settings.ack_frequency); } else if (send_ctl->ctl_ack_eliciting_pkt[pns] > 0 && !xqc_timer_is_set(&send_ctl->path_timer_manager, XQC_TIMER_ACK_INIT + pns)) diff --git a/src/transport/xqc_send_ctl.c b/src/transport/xqc_send_ctl.c index 7adf85c5d..5daf97322 100644 --- a/src/transport/xqc_send_ctl.c +++ b/src/transport/xqc_send_ctl.c @@ -18,6 +18,43 @@ #include "src/congestion_control/xqc_sample.h" #include "src/transport/xqc_pacing.h" #include "src/transport/xqc_utils.h" +#include "src/transport/xqc_datagram.h" + +int +xqc_send_ctl_may_remove_unacked_dgram(xqc_connection_t *conn, xqc_packet_out_t *po) +{ + // it is only called from loss detection function + // po must not be inflight + // po must be in unacked list + po->po_flag |= XQC_POF_DROPPED_DGRAM; + if (po->po_origin == NULL) { + if (po->po_origin_ref_cnt == 0) { + xqc_send_queue_remove_unacked(po, conn->conn_send_queue); + xqc_send_queue_insert_free(po, &conn->conn_send_queue->sndq_free_packets, conn->conn_send_queue); + return 1; + } + return 0; + } + + po->po_origin->po_flag |= XQC_POF_DROPPED_DGRAM; + if (po->po_origin->po_origin_ref_cnt >= 1) { + po->po_origin->po_origin_ref_cnt--; + } + + if (po->po_origin->po_origin_ref_cnt == 0) { + // po_origin must be in unacked list && po_origin must be ahead of po in unacked list + // if po_origin is still inflight, it will be removed when it is detected as lost + if (!(po->po_origin->po_flag & XQC_POF_IN_FLIGHT)) { + xqc_send_queue_remove_unacked(po->po_origin, conn->conn_send_queue); + xqc_send_queue_insert_free(po->po_origin, &conn->conn_send_queue->sndq_free_packets, conn->conn_send_queue); + } + } + // remove po: it must not be inflight + xqc_send_queue_remove_unacked(po, conn->conn_send_queue); + xqc_send_queue_insert_free(po, &conn->conn_send_queue->sndq_free_packets, conn->conn_send_queue); + + return 1; +} int xqc_send_ctl_indirectly_ack_po(xqc_connection_t *conn, xqc_packet_out_t *packet_out) @@ -63,8 +100,9 @@ xqc_send_ctl_create(xqc_path_ctx_t *path) send_ctl->ctl_minrtt = XQC_MAX_UINT32_VALUE; send_ctl->ctl_srtt = XQC_kInitialRtt * 1000; send_ctl->ctl_rttvar = XQC_kInitialRtt * 1000 / 2; + send_ctl->ctl_latest_rtt = 0; send_ctl->ctl_max_bytes_in_flight = 0; - send_ctl->ctl_reordering_packet_threshold = XQC_kPacketThreshold; + send_ctl->ctl_reordering_packet_threshold = conn->conn_settings.loss_detection_pkt_thresh; send_ctl->ctl_reordering_time_threshold_shift = XQC_kTimeThresholdShift; for (size_t i = 0; i < XQC_PNS_N; i++) { @@ -394,7 +432,7 @@ xqc_send_packet_cwnd_allows(xqc_send_ctl_t *send_ctl, /* packet with high priority first */ if (!xqc_send_ctl_can_send(send_ctl, packet_out, schedule_bytes)) { xqc_log(conn->log, XQC_LOG_DEBUG, - "|blocked by congestion control|"); + "|blocked by congestion control|po_sz:%ud|", packet_out->po_used_size); return XQC_FALSE; } } @@ -876,6 +914,10 @@ xqc_send_ctl_on_ack_received(xqc_send_ctl_t *send_ctl, xqc_pn_ctl_t *pn_ctl, xqc } } + if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + xqc_datagram_notify_ack(conn, packet_out); + } + xqc_send_ctl_on_packet_acked(send_ctl, packet_out, ack_recv_time, 1); xqc_send_queue_maybe_remove_unacked(packet_out, send_queue, NULL); @@ -1194,9 +1236,13 @@ xqc_send_ctl_detect_lost(xqc_send_ctl_t *send_ctl, xqc_send_queue_t *send_queue, /* 若 lost_pn == XQC_MAX_UINT64_VALUE, 无丢包 */ xqc_packet_number_t lost_pn = xqc_send_ctl_get_lost_sent_pn(send_ctl, pns); + xqc_int_t repair_dgram = 0; + xqc_list_for_each_safe(pos, next, &send_queue->sndq_unacked_packets[pns]) { po = xqc_list_entry(pos, xqc_packet_out_t, po_list); + repair_dgram = 0; + if (po->po_path_id != send_ctl->ctl_path->path_id) { continue; } @@ -1210,6 +1256,18 @@ xqc_send_ctl_detect_lost(xqc_send_ctl_t *send_ctl, xqc_send_queue_t *send_queue, continue; } + if (po->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + if (!(po->po_flag & XQC_POF_IN_FLIGHT)) { + if ((po->po_flag & XQC_POF_DROPPED_DGRAM) + || (po->po_origin && (po->po_origin->po_flag & XQC_POF_DROPPED_DGRAM))) + { + if (xqc_send_ctl_may_remove_unacked_dgram(conn, po)) { + continue; + } + } + } + } + /* Mark packet as lost, or set time when it should be marked. */ if (po->po_sent_time <= lost_send_time || (lost_pn != XQC_MAX_UINT64_VALUE && po->po_pkt.pkt_num <= lost_pn)) @@ -1217,13 +1275,28 @@ xqc_send_ctl_detect_lost(xqc_send_ctl_t *send_ctl, xqc_send_queue_t *send_queue, if (po->po_flag & XQC_POF_IN_FLIGHT) { xqc_send_ctl_decrease_inflight(conn, po); - /* if a packet don't need to be repair, don't retransmit it */ - if (!XQC_NEED_REPAIR(po->po_frame_types)) { - xqc_send_queue_remove_unacked(po, send_queue); - xqc_send_queue_insert_free(po, &send_queue->sndq_free_packets, send_queue); + if (po->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + send_ctl->ctl_lost_dgram_cnt++; + repair_dgram = xqc_datagram_notify_loss(conn, po); + if (conn->conn_settings.datagram_force_retrans_on) { + repair_dgram = XQC_DGRAM_RETX_ASKED_BY_APP; + } + } + + if (XQC_NEED_REPAIR(po->po_frame_types) + || repair_dgram == XQC_DGRAM_RETX_ASKED_BY_APP) + { + xqc_send_queue_copy_to_lost(po, send_queue); } else { - xqc_send_queue_copy_to_lost(po, send_queue); + if (po->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + xqc_send_ctl_may_remove_unacked_dgram(conn, po); + + } else { + /* if a packet does't need to be repair, don't retransmit it */ + xqc_send_queue_remove_unacked(po, send_queue); + xqc_send_queue_insert_free(po, &send_queue->sndq_free_packets, send_queue); + } } lost_n++; @@ -1469,10 +1542,16 @@ xqc_send_ctl_get_pto_time_and_space(xqc_send_ctl_t *send_ctl, xqc_usec_t now, xq xqc_usec_t pto_timeout = XQC_MAX_UINT64_VALUE; xqc_connection_t *c = send_ctl->ctl_conn; xqc_int_t pto_cnt = send_ctl->ctl_pto_count; + double backoff = xqc_send_ctl_pow_x(c->conn_settings.pto_backoff_factor, send_ctl->ctl_pto_count); /* get pto duration */ xqc_usec_t duration = (send_ctl->ctl_srtt - + xqc_max(4 * send_ctl->ctl_rttvar, XQC_kGranularity * 1000)) * xqc_send_ctl_pow(pto_cnt); + + xqc_max(4 * send_ctl->ctl_rttvar, XQC_kGranularity * 1000)) * backoff; + + xqc_log(c->log, XQC_LOG_DEBUG, + "|srtt:%ud|rtt_var:%ud|pto_duration:%ud|backoff_factor:%.2f|backoff:%.2f|pto_cnt:%d|max_ack_delay:%d|", + send_ctl->ctl_srtt, send_ctl->ctl_rttvar, duration, + c->conn_settings.pto_backoff_factor, backoff, pto_cnt, c->remote_settings.max_ack_delay); /* Arm PTO from now when there are no inflight packets */ if (send_ctl->ctl_bytes_in_flight == 0) { @@ -1506,7 +1585,7 @@ xqc_send_ctl_get_pto_time_and_space(xqc_send_ctl_t *send_ctl, xqc_usec_t now, xq break; } - duration += c->remote_settings.max_ack_delay * 1000 * xqc_send_ctl_pow(send_ctl->ctl_pto_count); + duration += c->remote_settings.max_ack_delay * 1000 * backoff; } t = send_ctl->ctl_time_of_last_sent_ack_eliciting_packet[pns] + duration; @@ -1697,6 +1776,10 @@ xqc_send_ctl_get_lost_sent_pn(xqc_send_ctl_t *send_ctl, xqc_pkt_num_space_t pns) } } + xqc_log(send_ctl->ctl_conn->log, XQC_LOG_DEBUG, + "|largest_acked:%ui|lost_pn:%ui|thresh:%ui|", + largest_acked, lost_pn, threshold); + return lost_pn; } diff --git a/src/transport/xqc_send_ctl.h b/src/transport/xqc_send_ctl.h index 87b5da97e..0051e393c 100644 --- a/src/transport/xqc_send_ctl.h +++ b/src/transport/xqc_send_ctl.h @@ -12,6 +12,7 @@ #include "src/transport/xqc_send_queue.h" #include "src/transport/xqc_timer.h" #include "src/transport/xqc_multipath.h" +#include #define XQC_kPacketThreshold 3 #define XQC_kTimeThresholdShift 3 @@ -28,6 +29,7 @@ /* 2^n */ #define xqc_send_ctl_pow(n) (1 << n) +#define xqc_send_ctl_pow_x(x, n) (fabs(x - 2) < 1e-7) ? xqc_send_ctl_pow(n) : pow(x, n) #define XQC_DEFAULT_RECORD_INTERVAL (100000) /* 100ms record interval */ @@ -129,6 +131,7 @@ typedef struct xqc_send_ctl_s { unsigned ctl_lost_count; unsigned ctl_tlp_count; unsigned ctl_spurious_loss_count; + unsigned ctl_lost_dgram_cnt; unsigned ctl_send_count_at_last_tra_path_status_changed_time; unsigned ctl_recv_count; @@ -173,6 +176,8 @@ xqc_send_ctl_calc_pto(xqc_send_ctl_t *send_ctl) + send_ctl->ctl_conn->local_settings.max_ack_delay * 1000; } +int xqc_send_ctl_may_remove_unacked_dgram(xqc_connection_t *conn, xqc_packet_out_t *po); + int xqc_send_ctl_indirectly_ack_po(xqc_connection_t *conn, xqc_packet_out_t *po); xqc_send_ctl_t *xqc_send_ctl_create(xqc_path_ctx_t *path); diff --git a/src/transport/xqc_send_queue.h b/src/transport/xqc_send_queue.h index dd59ca133..16ccc6070 100644 --- a/src/transport/xqc_send_queue.h +++ b/src/transport/xqc_send_queue.h @@ -5,7 +5,8 @@ #include "src/transport/xqc_conn.h" -#define XQC_SNDQ_PACKETS_USED_MAX 16000 +#define XQC_SNDQ_PACKETS_USED_MAX 18000 +#define XQC_SNDQ_RELEASE_ENOUGH_SPACE_TH 10 /* 1 / 10*/ typedef struct xqc_send_queue_s { @@ -29,6 +30,8 @@ typedef struct xqc_send_queue_s { size_t pkt_out_size; + xqc_bool_t sndq_full; + } xqc_send_queue_t; @@ -41,6 +44,12 @@ xqc_send_queue_can_write(xqc_send_queue_t *send_queue) return XQC_FALSE; } +static inline xqc_bool_t +xqc_send_queue_release_enough_space(xqc_send_queue_t *send_queue) +{ + return (send_queue->sndq_packets_used_max - send_queue->sndq_packets_used) + >= (send_queue->sndq_packets_used_max / XQC_SNDQ_RELEASE_ENOUGH_SPACE_TH); +} uint64_t xqc_send_queue_get_unsent_packets_num(xqc_send_queue_t *send_queue); diff --git a/src/transport/xqc_stream.c b/src/transport/xqc_stream.c index 1fefab496..d1aa2d31f 100644 --- a/src/transport/xqc_stream.c +++ b/src/transport/xqc_stream.c @@ -1096,8 +1096,8 @@ xqc_stream_recv(xqc_stream_t *stream, unsigned char *recv_buf, size_t recv_buf_s } - if (stream->stream_data_in.stream_length > 0 - && stream->stream_data_in.next_read_offset == stream->stream_data_in.stream_length) + if (stream->stream_data_in.stream_determined + && stream->stream_data_in.next_read_offset == stream->stream_data_in.stream_length) { *fin = 1; stream->stream_stats.peer_fin_read_time = xqc_monotonic_timestamp(); @@ -1167,6 +1167,7 @@ xqc_stream_send(xqc_stream_t *stream, unsigned char *send_data, size_t send_data stream->stream_flag |= XQC_STREAM_FLAG_HAS_0RTT; } else { + xqc_log(conn->log, XQC_LOG_DEBUG, "|blocked by no 0RTT support|"); ret = -XQC_EAGAIN; goto do_buff; } @@ -1183,6 +1184,7 @@ xqc_stream_send(xqc_stream_t *stream, unsigned char *send_data, size_t send_data } if (!xqc_send_queue_can_write(conn->conn_send_queue)) { + conn->conn_send_queue->sndq_full = XQC_TRUE; xqc_log(conn->log, XQC_LOG_DEBUG, "|too many packets used|sndq_packets_used:%ud|", conn->conn_send_queue->sndq_packets_used); ret = -XQC_EAGAIN; goto do_buff; @@ -1332,6 +1334,9 @@ xqc_stream_write_buffed_data_to_packets(xqc_stream_t *stream) return ret; } offset += send_data_written; + xqc_log(conn->log, XQC_LOG_DEBUG, + "|resend 1RTT stream packets|stream_id:%ui|offset:%uz|fin:%d|", + stream->stream_id, offset, fin); if (fin_only) { break; } @@ -1351,6 +1356,7 @@ xqc_process_write_streams(xqc_connection_t *conn) xqc_int_t ret; xqc_stream_t *stream; xqc_list_head_t *pos, *next; + int cnt = 0; xqc_list_for_each_safe(pos, next, &conn->conn_write_streams) { stream = xqc_list_entry(pos, xqc_stream_t, write_stream_list); @@ -1361,8 +1367,8 @@ xqc_process_write_streams(xqc_connection_t *conn) stream->stream_id, stream->stream_conn); continue; } - xqc_log(conn->log, XQC_LOG_DEBUG, "|stream_write_notify|flag:%d|stream_id:%ui|conn:%p|", - stream->stream_flag, stream->stream_id, stream->stream_conn); + xqc_log(conn->log, XQC_LOG_DEBUG, "|stream_write_notify|flag:%d|stream_id:%ui|conn:%p|cnt:%d|", + stream->stream_flag, stream->stream_id, stream->stream_conn, cnt++); if (stream->stream_if->stream_write_notify == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|stream_write_notify is NULL|flag:%d|stream_id:%ui|conn:%p|", stream->stream_flag, stream->stream_id, stream->stream_conn); diff --git a/src/transport/xqc_stream.h b/src/transport/xqc_stream.h index 2cebdbf69..60164cd53 100644 --- a/src/transport/xqc_stream.h +++ b/src/transport/xqc_stream.h @@ -81,6 +81,7 @@ typedef struct xqc_stream_data_in_s { uint64_t merged_offset_end; /* [0,end) */ uint64_t next_read_offset; /* next offset in stream */ uint64_t stream_length; + xqc_bool_t stream_determined; } xqc_stream_data_in_t; diff --git a/src/transport/xqc_timer.c b/src/transport/xqc_timer.c index 4100946bd..8cfd07349 100644 --- a/src/transport/xqc_timer.c +++ b/src/transport/xqc_timer.c @@ -363,6 +363,86 @@ xqc_timer_init(xqc_timer_manager_t *manager, xqc_log_t *log, void *user_data) timer->user_data = user_data; } } + + /* init gp timer list */ + xqc_init_list_head(&manager->gp_timer_list); + manager->next_gp_timer_id = 0; +} + +xqc_gp_timer_id_t xqc_timer_register_gp_timer(xqc_timer_manager_t *manager, + char *timer_name, xqc_gp_timer_timeout_pt cb, void *user_data) +{ + if (timer_name == NULL + || manager == NULL + || cb == NULL) + { + return -XQC_EPARAM; + } + + if (manager->next_gp_timer_id == XQC_GP_TIMER_ID_MAX) { + return XQC_ERROR; + } + + xqc_gp_timer_t *timer = (xqc_gp_timer_t*)xqc_calloc(1, sizeof(xqc_gp_timer_t)); + if (timer == NULL) { + return -XQC_EMALLOC; + } + + size_t name_len = strnlen(timer_name, 1024); + timer->name = (char*)xqc_calloc(1, name_len + 1); + if (timer->name == NULL) { + xqc_free(timer); + return -XQC_EMALLOC; + } + xqc_memcpy(timer->name, timer_name, name_len); + + timer->timer_is_set = XQC_FALSE; + timer->id = manager->next_gp_timer_id++; + timer->timeout_cb = cb; + timer->user_data = user_data; + + xqc_list_add_tail(&timer->list, &manager->gp_timer_list); + return timer->id; +} + +xqc_int_t +xqc_timer_unregister_gp_timer(xqc_timer_manager_t *manager, xqc_gp_timer_id_t gp_timer_id) +{ + if (!manager || gp_timer_id >= manager->next_gp_timer_id) { + return -XQC_EPARAM; + } + + xqc_list_head_t *pos, *next; + xqc_gp_timer_t *gp_timer; + + xqc_list_for_each_safe(pos, next, &manager->gp_timer_list) { + gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list); + if (gp_timer->id == gp_timer_id) { + xqc_timer_destroy_gp_timer(gp_timer); + return XQC_OK; + } + } + return XQC_ERROR; +} + +void +xqc_timer_destroy_gp_timer(xqc_gp_timer_t *gp_timer) +{ + xqc_list_del_init(&gp_timer->list); + xqc_free(gp_timer->name); + xqc_free(gp_timer); +} + +void +xqc_timer_destroy_gp_timer_list(xqc_timer_manager_t *manager) +{ + xqc_list_head_t *pos, *next; + xqc_gp_timer_t *gp_timer; + + xqc_list_for_each_safe(pos, next, &manager->gp_timer_list) { + gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list); + xqc_timer_destroy_gp_timer(gp_timer); + } } /* diff --git a/src/transport/xqc_timer.h b/src/transport/xqc_timer.h index e09ca4fad..4d0c77143 100644 --- a/src/transport/xqc_timer.h +++ b/src/transport/xqc_timer.h @@ -4,6 +4,7 @@ #include "src/common/xqc_time.h" #include "src/transport/xqc_packet.h" #include "src/transport/xqc_packet_in.h" +#include "src/common/xqc_list.h" /* * A connection will time out if no packets are sent or received for a @@ -47,10 +48,15 @@ typedef enum xqc_timer_type { } xqc_timer_type_t; +typedef int32_t xqc_gp_timer_id_t; + +#define XQC_GP_TIMER_ID_MAX (0x7fffffff) /* timer timeout callback */ typedef void (*xqc_timer_timeout_pt)(xqc_timer_type_t type, xqc_usec_t now, void *user_data); +typedef void (*xqc_gp_timer_timeout_pt)(xqc_gp_timer_id_t gp_timer_id, xqc_usec_t now, void *user_data); + typedef struct xqc_timer_s { uint8_t timer_is_set; xqc_usec_t expire_time; @@ -60,11 +66,105 @@ typedef struct xqc_timer_s { void *user_data; } xqc_timer_t; +/* general purpose timer */ +typedef struct xqc_gp_timer_s { + xqc_list_head_t list; + xqc_gp_timer_id_t id; + xqc_bool_t timer_is_set; + xqc_usec_t expire_time; + + /* callback function and user_data */ + xqc_gp_timer_timeout_pt timeout_cb; + void *user_data; + char *name; +} xqc_gp_timer_t; + typedef struct xqc_timer_manager_s { xqc_timer_t timer[XQC_TIMER_N]; xqc_log_t *log; + /* general purpose timer */ + xqc_list_head_t gp_timer_list; + xqc_gp_timer_id_t next_gp_timer_id; } xqc_timer_manager_t; +/* APIs for gp timer */ +xqc_gp_timer_id_t xqc_timer_register_gp_timer(xqc_timer_manager_t *manager, + char *timer_name, xqc_gp_timer_timeout_pt cb, void *user_data); + +xqc_int_t xqc_timer_unregister_gp_timer(xqc_timer_manager_t *manager, xqc_gp_timer_id_t gp_timer_id); + +void xqc_timer_destroy_gp_timer(xqc_gp_timer_t *gp_timer); + +void xqc_timer_destroy_gp_timer_list(xqc_timer_manager_t *manager); + +static inline xqc_int_t +xqc_timer_gp_timer_set(xqc_timer_manager_t *manager, xqc_gp_timer_id_t gp_timer_id, xqc_usec_t expire_time) +{ + if (!manager || gp_timer_id >= manager->next_gp_timer_id) { + return -XQC_EPARAM; + } + + xqc_list_head_t *pos, *next; + xqc_gp_timer_t *gp_timer; + + xqc_list_for_each_safe(pos, next, &manager->gp_timer_list) { + gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list); + if (gp_timer->id == gp_timer_id) { + gp_timer->expire_time = expire_time; + gp_timer->timer_is_set = XQC_TRUE; + xqc_log(manager->log, XQC_LOG_DEBUG, "|gp_timer_set|id:%d|name:%s|expire_time:%ui|", + gp_timer->id, gp_timer->name, gp_timer->expire_time); + return XQC_OK; + } + } + return XQC_ERROR; +} + +static inline xqc_int_t +xqc_timer_gp_timer_unset(xqc_timer_manager_t *manager, xqc_gp_timer_id_t gp_timer_id) +{ + if (!manager || gp_timer_id >= manager->next_gp_timer_id) { + return -XQC_EPARAM; + } + + xqc_list_head_t *pos, *next; + xqc_gp_timer_t *gp_timer; + + xqc_list_for_each_safe(pos, next, &manager->gp_timer_list) { + gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list); + if (gp_timer->id == gp_timer_id) { + gp_timer->expire_time = 0; + gp_timer->timer_is_set = XQC_FALSE; + xqc_log(manager->log, XQC_LOG_DEBUG, "|gp_timer_unset|id:%d|name:%s|expire_time:%ui|", + gp_timer->id, gp_timer->name, gp_timer->expire_time); + return XQC_OK; + } + } + return XQC_ERROR; +} + +static inline xqc_int_t +xqc_timer_gp_timer_get_info(xqc_timer_manager_t *manager, xqc_gp_timer_id_t gp_timer_id, xqc_bool_t *is_set, xqc_usec_t *expire_time) +{ + if (!manager || gp_timer_id >= manager->next_gp_timer_id) { + return -XQC_EPARAM; + } + + xqc_list_head_t *pos, *next; + xqc_gp_timer_t *gp_timer; + + xqc_list_for_each_safe(pos, next, &manager->gp_timer_list) { + gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list); + if (gp_timer->id == gp_timer_id) { + *is_set = gp_timer->timer_is_set; + *expire_time = gp_timer->expire_time; + xqc_log(manager->log, XQC_LOG_DEBUG, "|gp_timer_get_info|id:%d|name:%s|is_set:%d|expire_time:%ui|", + gp_timer->id, gp_timer->name, gp_timer->timer_is_set, gp_timer->expire_time); + return XQC_OK; + } + } + return XQC_ERROR; +} const char *xqc_timer_type_2_str(xqc_timer_type_t timer_type); @@ -149,6 +249,22 @@ xqc_timer_expire(xqc_timer_manager_t *manager, xqc_usec_t now) } } } + + /* expire gp timer */ + xqc_list_head_t *pos, *next; + xqc_gp_timer_t *gp_timer; + + xqc_list_for_each_safe(pos, next, &manager->gp_timer_list) { + gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list); + if (gp_timer->timer_is_set && gp_timer->expire_time <= now) { + xqc_log(manager->log, XQC_LOG_DEBUG, "|gp_timer_expire|id:%d|name:%s|expire_time:%ui|now:%ui|", + gp_timer->id, gp_timer->name, gp_timer->expire_time, now); + gp_timer->timeout_cb(gp_timer->id, now, gp_timer->user_data); + if (gp_timer->expire_time <= now) { + xqc_timer_gp_timer_unset(manager, gp_timer->id); + } + } + } } /* diff --git a/src/transport/xqc_transport_params.c b/src/transport/xqc_transport_params.c index 11438fef5..5e0b69e1a 100644 --- a/src/transport/xqc_transport_params.c +++ b/src/transport/xqc_transport_params.c @@ -162,6 +162,12 @@ xqc_transport_params_calc_length(const xqc_transport_params_t *params, xqc_put_varint_len(params->enable_multipath); } + if (params->max_datagram_frame_size) { + len += xqc_put_varint_len(XQC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE) + + xqc_put_varint_len(xqc_put_varint_len(params->max_datagram_frame_size)) + + xqc_put_varint_len(params->max_datagram_frame_size); + } + return len; } @@ -315,6 +321,11 @@ xqc_encode_transport_params(const xqc_transport_params_t *params, p = xqc_cpymem(p, params->retry_source_connection_id.cid_buf, params->retry_source_connection_id.cid_len); } + if (params->max_datagram_frame_size) { + p = xqc_put_varint_param(p, XQC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, + params->max_datagram_frame_size); + } + if (params->no_crypto) { p = xqc_put_varint_param(p, XQC_TRANSPORT_PARAM_NO_CRYPTO, params->no_crypto); @@ -568,6 +579,13 @@ xqc_decode_enable_multipath(xqc_transport_params_t *params, xqc_transport_params XQC_DECODE_VINT_VALUE(¶ms->enable_multipath, p, end); } +static xqc_int_t +xqc_decode_max_datagram_frame_size(xqc_transport_params_t *params, xqc_transport_params_type_t exttype, + const uint8_t *p, const uint8_t *end, uint64_t param_type, uint64_t param_len) +{ + XQC_DECODE_VINT_VALUE(¶ms->max_datagram_frame_size, p, end); +} + /* decode value from p, and store value in the input params */ typedef xqc_int_t (*xqc_trans_param_decode_func)( @@ -594,6 +612,7 @@ xqc_trans_param_decode_func xqc_trans_param_decode_func_list[] = { xqc_decode_retry_scid, xqc_decode_no_crypto, xqc_decode_enable_multipath, + xqc_decode_max_datagram_frame_size, }; @@ -628,6 +647,9 @@ xqc_trans_param_get_index(uint64_t param_type) case XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH: return XQC_TRANSPORT_PARAM_PROTOCOL_MAX + 1; + case XQC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE: + return XQC_TRANSPORT_PARAM_PROTOCOL_MAX + 2; + default: break; } @@ -716,6 +738,7 @@ xqc_decode_transport_params(xqc_transport_params_t *params, params->retry_source_connection_id.cid_len = 0; params->no_crypto = 0; + params->max_datagram_frame_size = 0; params->enable_multipath = 0; @@ -781,6 +804,10 @@ xqc_read_transport_params(char *tp_data, size_t tp_data_len, xqc_transport_param } else if (strncmp(p, "max_ack_delay=", xqc_lengthof("max_ack_delay=")) == 0) { p += xqc_lengthof("max_ack_delay="); params->max_ack_delay = strtoul(p, NULL, 10); + + } else if (strncmp(p, "max_datagram_frame_size=", xqc_lengthof("max_datagram_frame_size=")) == 0) { + p += xqc_lengthof("max_datagram_frame_size="); + params->max_datagram_frame_size = strtoul(p, NULL, 10); } p = strchr(p, '\n'); @@ -797,23 +824,51 @@ xqc_read_transport_params(char *tp_data, size_t tp_data_len, xqc_transport_param ssize_t xqc_write_transport_params(char *tp_buf, size_t cap, const xqc_transport_params_t *params) { - ssize_t tp_data_len = snprintf(tp_buf, cap, "initial_max_streams_bidi=%"PRIu64"\n" + char dgram_tp_str[256] = ""; + ssize_t tp_data_len = 0; + + if (params->max_datagram_frame_size) { + tp_data_len = snprintf(dgram_tp_str, 256, + "max_datagram_frame_size=%"PRIu64"\n", + params->max_datagram_frame_size); + if (tp_data_len < 0) { + return -XQC_ESYS; + } + } + + + tp_data_len = snprintf(tp_buf, cap, "initial_max_streams_bidi=%"PRIu64"\n" "initial_max_streams_uni=%"PRIu64"\n" "initial_max_stream_data_bidi_local=%"PRIu64"\n" "initial_max_stream_data_bidi_remote=%"PRIu64"\n" "initial_max_stream_data_uni=%"PRIu64"\n" "initial_max_data=%"PRIu64"\n" - "max_ack_delay=%"PRIu64"\n", + "max_ack_delay=%"PRIu64"\n" + "%s", params->initial_max_streams_bidi, params->initial_max_streams_uni, params->initial_max_stream_data_bidi_local, params->initial_max_stream_data_bidi_remote, params->initial_max_stream_data_uni, params->initial_max_data, - params->max_ack_delay); + params->max_ack_delay, + dgram_tp_str); + if (tp_data_len < 0) { return -XQC_ESYS; } return tp_data_len; } + +void +xqc_init_transport_params(xqc_transport_params_t *params) +{ + xqc_memzero(params, sizeof(xqc_transport_params_t)); + + /* like xqc_conn_init_trans_settings. */ + params->max_ack_delay = XQC_DEFAULT_MAX_ACK_DELAY; + params->ack_delay_exponent = XQC_DEFAULT_ACK_DELAY_EXPONENT; + params->max_udp_payload_size = XQC_DEFAULT_MAX_UDP_PAYLOAD_SIZE; + params->active_connection_id_limit = XQC_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; +} diff --git a/src/transport/xqc_transport_params.h b/src/transport/xqc_transport_params.h index 08dc650b5..dd6b667f8 100644 --- a/src/transport/xqc_transport_params.h +++ b/src/transport/xqc_transport_params.h @@ -62,6 +62,9 @@ typedef enum { /* upper limit of params defined in [Transport] */ XQC_TRANSPORT_PARAM_PROTOCOL_MAX, + /* max datagram frame size */ + XQC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020, + /* do no cryption on 0-RTT and 1-RTT packets */ XQC_TRANSPORT_PARAM_NO_CRYPTO = 0x1000, /* multipath quic attributes */ @@ -111,6 +114,13 @@ typedef struct { xqc_cid_t retry_source_connection_id; uint8_t retry_source_connection_id_present; + /* + * support for datagram (RFC 9221). + * default: 0, not supported + * special: 65535, accept datagram frames with any length in a QUIC packet + */ + uint64_t max_datagram_frame_size; + /** * no_crypto is a self-defined experimental transport parameter by xquic, xquic will do no * encryption on 0-RTT or 1-RTT packets if no_crypto is set to be 1. @@ -165,5 +175,7 @@ xqc_int_t xqc_read_transport_params(char *tp_data, size_t tp_data_len, ssize_t xqc_write_transport_params(char *tp_buf, size_t cap, const xqc_transport_params_t *params); +void xqc_init_transport_params(xqc_transport_params_t *params); + #endif /* XQC_TRANSPORT_PARAMS_H_ */ \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e72200eeb..35da86a27 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,8 @@ if(HAVE_CUNIT) ${UNIT_TEST_DIR}/xqc_id_hash_test.c ${UNIT_TEST_DIR}/xqc_sent_record_test.c ${UNIT_TEST_DIR}/xqc_retry_test.c + ${UNIT_TEST_DIR}/xqc_datagram_test.c + ${UNIT_TEST_DIR}/xqc_h3_ext_test.c ) diff --git a/tests/test_client.c b/tests/test_client.c index 920e149fd..56daaaaf4 100644 --- a/tests/test_client.c +++ b/tests/test_client.c @@ -20,6 +20,7 @@ #include #include #include +#include int printf_null(const char *format, ...) @@ -55,6 +56,18 @@ printf_null(const char *format, ...) typedef struct user_conn_s user_conn_t; + +#define XQC_TEST_DGRAM_BATCH_SZ 32 + +typedef struct user_datagram_block_s { + unsigned char *recv_data; + unsigned char *data; + size_t data_len; + size_t data_sent; + size_t data_recv; + size_t data_lost; + size_t dgram_lost; +} user_dgram_blk_t; typedef struct client_ctx_s { xqc_engine_t *engine; struct event *ev_engine; @@ -67,26 +80,33 @@ typedef struct client_ctx_s { } client_ctx_t; typedef struct user_stream_s { - xqc_stream_t *stream; - xqc_h3_request_t *h3_request; - user_conn_t *user_conn; - uint64_t send_offset; - int header_sent; - int header_recvd; - char *send_body; - size_t send_body_len; - size_t send_body_max; - char *recv_body; - size_t recv_body_len; - FILE *recv_body_fp; - int recv_fin; - xqc_msec_t start_time; - xqc_msec_t first_frame_time; /* first frame download time */ - xqc_msec_t last_read_time; - int abnormal_count; - int body_read_notify_cnt; - xqc_msec_t last_recv_log_time; - uint64_t recv_log_bytes; + xqc_stream_t *stream; + xqc_h3_request_t *h3_request; + user_conn_t *user_conn; + uint64_t send_offset; + int header_sent; + int header_recvd; + char *send_body; + size_t send_body_len; + size_t send_body_max; + char *recv_body; + size_t recv_body_len; + FILE *recv_body_fp; + int recv_fin; + xqc_msec_t start_time; + xqc_msec_t first_frame_time; /* first frame download time */ + xqc_msec_t last_read_time; + int abnormal_count; + int body_read_notify_cnt; + xqc_msec_t last_recv_log_time; + uint64_t recv_log_bytes; + + xqc_h3_ext_bytestream_t *h3_ext_bs; + struct event *ev_bytestream_timer; + + int snd_times; + int rcv_times; + } user_stream_t; typedef struct user_conn_s { @@ -112,6 +132,14 @@ typedef struct user_conn_s { struct event *ev_epoch; int h3; + + user_dgram_blk_t *dgram_blk; + size_t dgram_mss; + uint8_t dgram_not_supported; + int dgram_retry_in_hs_cb; + + xqc_connection_t *quic_conn; + xqc_h3_conn_t *h3_conn; client_ctx_t *ctx; int cur_stream_num; } user_conn_t; @@ -156,9 +184,18 @@ int g_test_qch_mode = 0; int g_random_cid = 0; xqc_conn_settings_t *g_conn_settings; +unsigned char *sock_op_buffer[2000]; +size_t sock_op_buffer_len = 0; +size_t dgram1_size = 0; +size_t dgram2_size = 0; + +int dgram_drop_pkt1 = 0; client_ctx_t ctx; struct event_base *eb; +int g_send_dgram; +int g_max_dgram_size; int g_req_cnt; +int g_bytestream_cnt; int g_req_max; int g_send_body_size; int g_send_body_size_defined; @@ -175,6 +212,10 @@ int g_is_get; uint64_t g_last_sock_op_time; //currently, the maximum used test case id is 19 //please keep this comment updated if you are adding more test cases. :-D +//99 for pure fin +//2XX for datagram testcases +//3XX for h3 ext bytestream testcases +//4XX for conn_settings configuration int g_test_case; int g_ipv6; int g_no_crypt; @@ -201,6 +242,8 @@ int g_epoch = 0; int g_cur_epoch = 0; int g_mp_backup_mode = 0; int g_mp_request_accelerate = 0; +double g_copa_ai = 1.0; +double g_copa_delta = 0.05; char g_header_key[MAX_HEADER_KEY_LEN]; char g_header_value[MAX_HEADER_VALUE_LEN]; @@ -225,11 +268,347 @@ static uint64_t last_recv_ts = 0; static void xqc_client_socket_event_callback(int fd, short what, void *arg); static void xqc_client_timeout_callback(int fd, short what, void *arg); static void xqc_client_abs_timeout_callback(int, short, void*); +static void xqc_client_bytestream_timeout_callback(int, short, void*); /* 用于路径增删debug */ static void xqc_client_path_callback(int fd, short what, void *arg); static void xqc_client_epoch_callback(int fd, short what, void *arg); +/* */ + +static void +xqc_client_datagram_send(user_conn_t *user_conn) +{ + if (user_conn->dgram_not_supported) { + // exit + printf("[dgram]|peer_does_not_support_datagram|\n"); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } + + // try to send 0rtt datagram while the client does not have 0rtt transport parameters + if (g_test_case == 202) { + if (user_conn->dgram_mss == 0) { + user_conn->dgram_mss = 1000; + } + } + + if (user_conn->dgram_mss == 0) { + user_conn->dgram_retry_in_hs_cb = 1; + printf("[dgram]|waiting_for_max_datagram_frame_size_from_peer|please_retry_in_hs_callback|\n"); + return; + } + + user_dgram_blk_t *dgram_blk = user_conn->dgram_blk; + int ret; + + if (g_send_dgram == 1) { + if (g_test_case == 203 && user_conn->dgram_mss) { + g_test_case = -1; + user_conn->dgram_mss++; + } + uint64_t dgram_id; + while (dgram_blk->data_sent < dgram_blk->data_len) { + size_t dgram_size = dgram_blk->data_len - dgram_blk->data_sent; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + dgram_blk->data[dgram_blk->data_sent] = 0x31; + ret = xqc_datagram_send(user_conn->quic_conn, dgram_blk->data + dgram_blk->data_sent, dgram_size, &dgram_id, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EAGAIN) { + printf("[dgram]|retry_datagram_send_later|\n"); + return; + } else if (ret == -XQC_EDGRAM_TOO_LARGE ) { + printf("[dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, dgram_size, xqc_datagram_get_mss(user_conn->quic_conn)); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0) { + printf("[dgram]|send_datagram_error|err_code:%d|\n", ret); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } + //printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id, dgram_size); + dgram_blk->data_sent += dgram_size; + } + } else if (g_send_dgram == 2) { + struct iovec iov[XQC_TEST_DGRAM_BATCH_SZ]; + uint64_t dgram_id_list[XQC_TEST_DGRAM_BATCH_SZ]; + size_t bytes_in_batch = 0; + int batch_cnt = 0; + while ((dgram_blk->data_sent + bytes_in_batch) < dgram_blk->data_len) { + if (batch_cnt == 1) { + if (g_test_case == 203 && user_conn->dgram_mss) { + g_test_case = -1; + user_conn->dgram_mss++; + } + } + size_t dgram_size = dgram_blk->data_len - dgram_blk->data_sent - bytes_in_batch; + size_t succ_sent = 0, succ_sent_bytes = 0; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + iov[batch_cnt].iov_base = dgram_blk->data + dgram_blk->data_sent + bytes_in_batch; + iov[batch_cnt].iov_len = dgram_size; + dgram_blk->data[dgram_blk->data_sent + bytes_in_batch] = 0x31; + bytes_in_batch += dgram_size; + batch_cnt++; + if ((bytes_in_batch + dgram_blk->data_sent) == dgram_blk->data_len + || batch_cnt == XQC_TEST_DGRAM_BATCH_SZ) + { + ret = xqc_datagram_send_multiple(user_conn->quic_conn, iov, dgram_id_list, batch_cnt, &succ_sent, &succ_sent_bytes, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EDGRAM_TOO_LARGE) { + printf("[dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, iov[succ_sent].iov_len, xqc_datagram_get_mss(user_conn->quic_conn)); + printf("[dgram]|partially_sent_pkts_in_a_batch|cnt:%zu|\n", succ_sent); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0 && ret != -XQC_EAGAIN) { + printf("[dgram]|send_datagram_multiple_error|err_code:%d|\n", ret); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } + + // for (int i = 0; i < succ_sent; i++) { + // printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id_list[i], iov[i].iov_len); + // } + + // printf("[dgram]|datagrams_sent_in_a_batch|cnt:%zu|size:%zu|\n", succ_sent, succ_sent_bytes); + + dgram_blk->data_sent += succ_sent_bytes; + + if (ret == -XQC_EAGAIN) { + printf("[dgram]|retry_datagram_send_multiple_later|\n"); + return; + } + + bytes_in_batch = 0; + batch_cnt = 0; + } + } + + } +} + + +static void +xqc_client_datagram_read_callback(xqc_connection_t *conn, void *user_data, const void *data, size_t data_len, uint64_t dgram_ts) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + if (g_echo_check) { + memcpy(user_conn->dgram_blk->recv_data + user_conn->dgram_blk->data_recv, data, data_len); + } + user_conn->dgram_blk->data_recv += data_len; + //printf("[dgram]|read_data|size:%zu|recv_time:%"PRIu64"|\n", data_len, dgram_ts); + if (g_test_case == 206 && g_no_crypt) { + if (dgram1_size == 0) { + dgram1_size = data_len; + } else { + if (dgram2_size == 0) { + dgram2_size = data_len; + } + } + } +} + +static void +xqc_client_datagram_write_callback(xqc_connection_t *conn, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + if (g_send_dgram) { + printf("[dgram]|dgram_write|\n"); + xqc_client_datagram_send(user_conn); + } +} + +static void +xqc_client_datagram_acked_callback(xqc_connection_t *conn, uint64_t dgram_id, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + if (g_test_case == 207) { + printf("[dgram]|dgram_acked|dgram_id:%"PRIu64"|\n", dgram_id); + g_test_case = -1; + } +} + +static int +xqc_client_datagram_lost_callback(xqc_connection_t *conn, uint64_t dgram_id, void *user_data) +{ + //printf("[dgram]|dgram_lost|dgram_id:%"PRIu64"|\n", dgram_id); + user_conn_t *user_conn = (user_conn_t*)user_data; + //user_conn->dgram_blk->data_lost += data_len; + user_conn->dgram_blk->dgram_lost++; + if (g_test_case == 205 && g_no_crypt) { + printf("[dgram]|dgram_lost|dgram_id:%"PRIu64"|\n", dgram_id); + } + return 0; +} + + +static void +xqc_client_h3_ext_datagram_send(user_conn_t *user_conn) +{ + if (user_conn->dgram_not_supported) { + // exit + printf("[h3-dgram]|peer_does_not_support_datagram|\n"); + // xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } + + // try to send 0rtt datagram while the client does not have 0rtt transport parameters + if (g_test_case == 202) { + if (user_conn->dgram_mss == 0) { + user_conn->dgram_mss = 1000; + } + } + + if (user_conn->dgram_mss == 0) { + user_conn->dgram_retry_in_hs_cb = 1; + printf("[h3-dgram]|waiting_for_max_datagram_frame_size_from_peer|please_retry_in_hs_callback|\n"); + return; + } + + user_dgram_blk_t *dgram_blk = user_conn->dgram_blk; + int ret; + + if (g_send_dgram == 1) { + if (g_test_case == 203 && user_conn->dgram_mss) { + g_test_case = -1; + user_conn->dgram_mss++; + } + uint64_t dgram_id; + while (dgram_blk->data_sent < dgram_blk->data_len) { + size_t dgram_size = dgram_blk->data_len - dgram_blk->data_sent; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + dgram_blk->data[dgram_blk->data_sent] = 0x31; + ret = xqc_h3_ext_datagram_send(user_conn->h3_conn, dgram_blk->data + dgram_blk->data_sent, dgram_size, &dgram_id, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EAGAIN) { + printf("[h3-dgram]|retry_datagram_send_later|\n"); + return; + } else if (ret == -XQC_EDGRAM_TOO_LARGE ) { + printf("[h3-dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, dgram_size, xqc_h3_ext_datagram_get_mss(user_conn->h3_conn)); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0) { + printf("[h3-dgram]|send_datagram_error|err_code:%d|\n", ret); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } + //printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id, dgram_size); + dgram_blk->data_sent += dgram_size; + } + } else if (g_send_dgram == 2) { + struct iovec iov[XQC_TEST_DGRAM_BATCH_SZ]; + uint64_t dgram_id_list[XQC_TEST_DGRAM_BATCH_SZ]; + size_t bytes_in_batch = 0; + int batch_cnt = 0; + while ((dgram_blk->data_sent + bytes_in_batch) < dgram_blk->data_len) { + if (batch_cnt == 1) { + if (g_test_case == 203 && user_conn->dgram_mss) { + g_test_case = -1; + user_conn->dgram_mss++; + } + } + size_t dgram_size = dgram_blk->data_len - dgram_blk->data_sent - bytes_in_batch; + size_t succ_sent = 0, succ_sent_bytes = 0; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + iov[batch_cnt].iov_base = dgram_blk->data + dgram_blk->data_sent + bytes_in_batch; + iov[batch_cnt].iov_len = dgram_size; + dgram_blk->data[dgram_blk->data_sent + bytes_in_batch] = 0x31; + bytes_in_batch += dgram_size; + batch_cnt++; + if ((bytes_in_batch + dgram_blk->data_sent) == dgram_blk->data_len + || batch_cnt == XQC_TEST_DGRAM_BATCH_SZ) + { + ret = xqc_h3_ext_datagram_send_multiple(user_conn->h3_conn, iov, dgram_id_list, batch_cnt, &succ_sent, &succ_sent_bytes, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EDGRAM_TOO_LARGE) { + printf("[h3-dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, iov[succ_sent].iov_len, xqc_h3_ext_datagram_get_mss(user_conn->h3_conn)); + printf("[h3-dgram]|partially_sent_pkts_in_a_batch|cnt:%zu|\n", succ_sent); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0 && ret != -XQC_EAGAIN) { + printf("[h3-dgram]|send_datagram_multiple_error|err_code:%d|\n", ret); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } + + // for (int i = 0; i < succ_sent; i++) { + // printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id_list[i], iov[i].iov_len); + // } + + // printf("[dgram]|datagrams_sent_in_a_batch|cnt:%zu|size:%zu|\n", succ_sent, succ_sent_bytes); + + dgram_blk->data_sent += succ_sent_bytes; + + if (ret == -XQC_EAGAIN) { + printf("[h3-dgram]|retry_datagram_send_multiple_later|\n"); + return; + } + + bytes_in_batch = 0; + batch_cnt = 0; + } + } + + } +} + +static void +xqc_client_h3_ext_datagram_read_callback(xqc_h3_conn_t *conn, const void *data, size_t data_len, void *user_data, uint64_t ts) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + if (g_echo_check) { + memcpy(user_conn->dgram_blk->recv_data + user_conn->dgram_blk->data_recv, data, data_len); + } + user_conn->dgram_blk->data_recv += data_len; + // printf("[h3-dgram]|read_data|size:%zu|recv_time:%"PRIu64"|\n", data_len, ts); + if (g_test_case == 206 && g_no_crypt) { + if (dgram1_size == 0) { + dgram1_size = data_len; + } else { + if (dgram2_size == 0) { + dgram2_size = data_len; + } + } + } +} + +static void +xqc_client_h3_ext_datagram_write_callback(xqc_h3_conn_t *conn, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + if (g_send_dgram) { + printf("[h3-dgram]|dgram_write|\n"); + xqc_client_h3_ext_datagram_send(user_conn); + } +} + +static void +xqc_client_h3_ext_datagram_acked_callback(xqc_h3_conn_t *conn, uint64_t dgram_id, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + if (g_test_case == 207) { + printf("[h3-dgram]|dgram_acked|dgram_id:%"PRIu64"|\n", dgram_id); + g_test_case = -1; + } + // printf("[h3-dgram]|latest_rtt:%"PRIu64"|\n", xqc_conn_get_lastest_rtt(ctx.engine, &user_conn->cid)); +} + +static int +xqc_client_h3_ext_datagram_lost_callback(xqc_h3_conn_t *conn, uint64_t dgram_id, void *user_data) +{ + //printf("[dgram]|dgram_lost|dgram_id:%"PRIu64"|\n", dgram_id); + user_conn_t *user_conn = (user_conn_t*)user_data; + user_conn->dgram_blk->data_lost += 0; + user_conn->dgram_blk->dgram_lost++; + if (g_test_case == 205 && g_no_crypt) { + printf("[h3-dgram]|dgram_lost|dgram_id:%"PRIu64"|\n", dgram_id); + } + return 0; +} + static void xqc_client_timeout_multi_process_callback(int fd, short what, void *arg); /* @@ -502,6 +881,42 @@ xqc_client_write_socket(const unsigned char *buf, size_t size, printf("test case 23, corrupt byte[15]\n"); } + // drop the first datagram packet + if ((g_test_case == 205 || g_test_case == 206) && g_no_crypt && !dgram_drop_pkt1) { + int header_type = send_buf[0] & 0x80; + if (header_type == 0x80) { + // long header: 29B + 3B (frame header) + int lp_type = send_buf[0] & 0x30; + if (lp_type == 0x10) { + //0RTT pkt + if (send_buf[29] == 0x31) { + //datagram frame + if (g_test_case == 206) { + //hold data & swap the order with the next one + //swap 1st & 2nd dgram + memcpy(sock_op_buffer, send_buf, send_buf_size); + sock_op_buffer_len = send_buf_size; + } + dgram_drop_pkt1 = 1; + return send_buf_size; + } + } + } else { + // short header: 13B + 3B (frame header) + if (send_buf[13] == 0x31) { + //datagram frame + if (g_test_case == 206) { + //hold data & swap the order with the next one + //swap 1st & 2nd dgram + memcpy(sock_op_buffer, send_buf, send_buf_size); + sock_op_buffer_len = send_buf_size; + } + dgram_drop_pkt1 = 1; + return send_buf_size; + } + } + } + res = sendto(fd, send_buf, send_buf_size, 0, peer_addr, peer_addrlen); if (res < 0) { printf("xqc_client_write_socket err %zd %s\n", res, strerror(errno)); @@ -509,6 +924,32 @@ xqc_client_write_socket(const unsigned char *buf, size_t size, res = XQC_SOCKET_EAGAIN; } } + + if (sock_op_buffer_len) { + int header_type = send_buf[0] & 0x80; + int frame_type = -1; + if (header_type == 0x80) { + // long header: 29B + 3B (frame header) + int lp_type = send_buf[0] & 0x30; + if (lp_type == 0x10) { + //0RTT pkt + frame_type = send_buf[29]; + } + } else { + frame_type = send_buf[13]; + } + if (frame_type == 0x31) { + int tmp = sendto(fd, sock_op_buffer, sock_op_buffer_len, 0, peer_addr, peer_addrlen); + if (tmp < 0) { + res = tmp; + printf("xqc_client_write_socket err %zd %s\n", res, strerror(errno)); + if (errno == EAGAIN) { + res = XQC_SOCKET_EAGAIN; + } + } + sock_op_buffer_len = 0; + } + } } while ((res < 0) && (errno == EINTR)); return res; @@ -613,6 +1054,42 @@ xqc_client_write_socket_ex(uint64_t path_id, printf("test case 23, corrupt byte[15]\n"); } + // drop the first datagram packet + if ((g_test_case == 205 || g_test_case == 206) && g_no_crypt && !dgram_drop_pkt1) { + int header_type = send_buf[0] & 0x80; + if (header_type == 0x80) { + // long header: 29B + 3B (frame header) + int lp_type = send_buf[0] & 0x30; + if (lp_type == 0x10) { + //0RTT pkt + if (send_buf[29] == 0x31) { + //datagram frame + if (g_test_case == 206) { + //hold data & swap the order with the next one + //swap 1st & 2nd dgram + memcpy(sock_op_buffer, send_buf, send_buf_size); + sock_op_buffer_len = send_buf_size; + } + dgram_drop_pkt1 = 1; + return send_buf_size; + } + } + } else { + // short header: 13B + 3B (frame header) + if (send_buf[13] == 0x31) { + //datagram frame + if (g_test_case == 206) { + //hold data & swap the order with the next one + //swap 1st & 2nd dgram + memcpy(sock_op_buffer, send_buf, send_buf_size); + sock_op_buffer_len = send_buf_size; + } + dgram_drop_pkt1 = 1; + return send_buf_size; + } + } + } + res = sendto(fd, send_buf, send_buf_size, 0, peer_addr, peer_addrlen); if (res < 0) { printf("xqc_client_write_socket_ex path:%lu err %zd %s\n", path_id, res, strerror(errno)); @@ -622,6 +1099,33 @@ xqc_client_write_socket_ex(uint64_t path_id, res = XQC_SOCKET_ERROR; } } + + if (sock_op_buffer_len) { + int header_type = send_buf[0] & 0x80; + int frame_type = -1; + if (header_type == 0x80) { + // long header: 29B + 3B (frame header) + int lp_type = send_buf[0] & 0x30; + if (lp_type == 0x10) { + //0RTT pkt + frame_type = send_buf[29]; + } + } else { + frame_type = send_buf[13]; + } + if (frame_type == 0x31) { + int tmp = sendto(fd, sock_op_buffer, sock_op_buffer_len, 0, peer_addr, peer_addrlen); + if (tmp < 0) { + res = tmp; + printf("xqc_client_write_socket err %zd %s\n", res, strerror(errno)); + if (errno == EAGAIN) { + res = XQC_SOCKET_EAGAIN; + } + } + sock_op_buffer_len = 0; + } + } + } while ((res < 0) && (errno == EINTR)); return res; @@ -907,7 +1411,7 @@ xqc_client_user_conn_multi_process_create(client_ctx_t *ctx, const char *server_ { user_conn_t *user_conn = calloc(1, sizeof(user_conn_t)); /* use HTTP3? */ - user_conn->h3 = transport ? 0 : 1; + user_conn->h3 = transport; user_conn->ctx = ctx; user_conn->ev_timeout = event_new(ctx->eb, -1, 0, xqc_client_timeout_multi_process_callback, user_conn); @@ -941,7 +1445,7 @@ xqc_client_user_conn_create(const char *server_addr, int server_port, user_conn_t *user_conn = calloc(1, sizeof(user_conn_t)); /* use HTTP3? */ - user_conn->h3 = transport ? 0 : 1; + user_conn->h3 = transport; user_conn->ev_timeout = event_new(eb, -1, 0, xqc_client_timeout_callback, user_conn); /* set connection timeout */ @@ -965,6 +1469,7 @@ xqc_client_user_conn_create(const char *server_addr, int server_port, event_add(user_conn->ev_abs_timeout, &tv); } + user_conn->conn_create_time = now(); int ip_type = (g_ipv6 ? AF_INET6 : AF_INET); @@ -1039,6 +1544,13 @@ xqc_client_conn_create_notify(xqc_connection_t *conn, const xqc_cid_t *cid, void user_conn_t *user_conn = (user_conn_t *)user_data; xqc_conn_set_alp_user_data(conn, user_conn); + user_conn->dgram_mss = xqc_datagram_get_mss(conn); + user_conn->quic_conn = conn; + + if (g_test_case == 200 || g_test_case == 201) { + printf("[dgram-200]|0RTT|initial_mss:%zu|\n", user_conn->dgram_mss); + } + printf("xqc_conn_is_ready_to_send_early_data:%d\n", xqc_conn_is_ready_to_send_early_data(conn)); return 0; } @@ -1048,7 +1560,7 @@ xqc_client_conn_close_notify(xqc_connection_t *conn, const xqc_cid_t *cid, void { DEBUG; - user_conn_t *user_conn = (user_conn_t *)conn_proto_data; + user_conn_t *user_conn = (user_conn_t *)user_data; client_ctx_t *p_ctx; if (g_test_qch_mode) { @@ -1057,12 +1569,22 @@ xqc_client_conn_close_notify(xqc_connection_t *conn, const xqc_cid_t *cid, void p_ctx = &ctx; } + xqc_int_t err = xqc_conn_get_errno(conn); + printf("should_clear_0rtt_ticket, conn_err:%d, clear_0rtt_ticket:%d\n", err, xqc_conn_should_clear_0rtt_ticket(err)); + xqc_conn_stats_t stats = xqc_conn_get_stats(p_ctx->engine, cid); printf("send_count:%u, lost_count:%u, tlp_count:%u, recv_count:%u, srtt:%"PRIu64" early_data_flag:%d, conn_err:%d, mp_state:%d, ack_info:%s\n", stats.send_count, stats.lost_count, stats.tlp_count, stats.recv_count, stats.srtt, stats.early_data_flag, stats.conn_err, stats.mp_state, stats.ack_info); printf("conn_info: \"%s\"\n", stats.conn_info); + if (!g_test_qch_mode) { + printf("[dgram]|recv_dgram_bytes:%zu|sent_dgram_bytes:%zu|lost_dgram_bytes:%zu|lost_cnt:%zu|\n", + user_conn->dgram_blk->data_recv, user_conn->dgram_blk->data_sent, + user_conn->dgram_blk->data_lost, user_conn->dgram_blk->dgram_lost); + } + + if (g_test_qch_mode) { if (p_ctx->cur_conn_num == 0) { event_base_loopbreak(p_ctx->eb); @@ -1114,6 +1636,23 @@ xqc_client_conn_handshake_finished(xqc_connection_t *conn, void *user_data, void } hsk_completed = 1; + + user_conn->dgram_mss = xqc_datagram_get_mss(conn); + if (user_conn->dgram_mss == 0) { + user_conn->dgram_not_supported = 1; + if (g_test_case == 204) { + user_conn->dgram_not_supported = 0; + user_conn->dgram_mss = 1000; + } + } + + if (g_test_case == 200 || g_test_case == 201) { + printf("[dgram-200]|1RTT|updated_mss:%zu|\n", user_conn->dgram_mss); + } + + if (g_send_dgram && user_conn->dgram_retry_in_hs_cb) { + xqc_client_datagram_send(user_conn); + } } int @@ -1149,6 +1688,13 @@ xqc_client_h3_conn_create_notify(xqc_h3_conn_t *conn, const xqc_cid_t *cid, void xqc_h3_conn_set_settings(conn, &settings); } + user_conn->dgram_mss = xqc_h3_ext_datagram_get_mss(conn); + user_conn->h3_conn = conn; + + if (g_test_case == 200 || g_test_case == 201) { + printf("[h3-dgram-200]|0RTT|initial_mss:%zu|\n", user_conn->dgram_mss); + } + printf("xqc_h3_conn_is_ready_to_send_early_data:%d\n", xqc_h3_conn_is_ready_to_send_early_data(conn)); return 0; } @@ -1169,10 +1715,20 @@ xqc_client_h3_conn_close_notify(xqc_h3_conn_t *conn, const xqc_cid_t *cid, void p_ctx = &ctx; } + xqc_int_t err = xqc_h3_conn_get_errno(conn); + printf("should_clear_0rtt_ticket, conn_err:%d, clear_0rtt_ticket:%d\n", err, xqc_conn_should_clear_0rtt_ticket(err)); + xqc_conn_stats_t stats = xqc_conn_get_stats(p_ctx->engine, cid); printf("send_count:%u, lost_count:%u, tlp_count:%u, recv_count:%u, srtt:%"PRIu64" early_data_flag:%d, conn_err:%d, mp_state:%d, ack_info:%s, conn_info:%s\n", stats.send_count, stats.lost_count, stats.tlp_count, stats.recv_count, stats.srtt, stats.early_data_flag, stats.conn_err, stats.mp_state, stats.ack_info, stats.conn_info); + if (!g_test_qch_mode) { + printf("[h3-dgram]|recv_dgram_bytes:%zu|sent_dgram_bytes:%zu|lost_dgram_bytes:%zu|lost_cnt:%zu|\n", + user_conn->dgram_blk->data_recv, user_conn->dgram_blk->data_sent, + user_conn->dgram_blk->data_lost, user_conn->dgram_blk->dgram_lost); + } + + if (g_test_qch_mode) { if (p_ctx->cur_conn_num == 0) { event_base_loopbreak(p_ctx->eb); @@ -1209,6 +1765,23 @@ xqc_client_h3_conn_handshake_finished(xqc_h3_conn_t *h3_conn, void *user_data) printf("====>SCID:%s\n", xqc_scid_str(&user_conn->cid)); hsk_completed = 1; + + user_conn->dgram_mss = xqc_h3_ext_datagram_get_mss(h3_conn); + if (user_conn->dgram_mss == 0) { + user_conn->dgram_not_supported = 1; + if (g_test_case == 204) { + user_conn->dgram_not_supported = 0; + user_conn->dgram_mss = 1000; + } + } + + if (g_test_case == 200 || g_test_case == 201) { + printf("[h3-dgram-200]|1RTT|updated_mss:%zu|\n", user_conn->dgram_mss); + } + + if (g_send_dgram && user_conn->dgram_retry_in_hs_cb) { + xqc_client_h3_ext_datagram_send(user_conn); + } } void @@ -1244,6 +1817,11 @@ xqc_client_stream_send(xqc_stream_t *stream, void *user_data) static int send_cnt = 0; printf("|xqc_client_stream_send|cnt:%d|\n", ++send_cnt); + if (g_test_case == 99) { + xqc_stream_send(stream, NULL, 0, 1); + return 0; + } + ssize_t ret; user_stream_t *user_stream = (user_stream_t *) user_data; @@ -1476,6 +2054,176 @@ xqc_client_stream_close_notify(xqc_stream_t *stream, void *user_data) return 0; } +int +xqc_client_bytestream_send(xqc_h3_ext_bytestream_t *h3_bs, user_stream_t *user_stream) +{ + if (user_stream->start_time == 0) { + user_stream->start_time = now(); + } + ssize_t ret = 0; + char content_len[10]; + + if (g_test_case == 302 || g_test_case == 310) { + //send pure fin on bytestream + xqc_h3_ext_bytestream_finish(h3_bs); + return 0; + } + + if (g_test_case == 303) { + //send pure fin on bytestream + xqc_h3_ext_bytestream_send(h3_bs, NULL, 0, 1, XQC_DATA_QOS_HIGH); + return 0; + } + + if (user_stream->send_body == NULL) { + user_stream->send_body_max = MAX_BUF_SIZE; + user_stream->send_body_len = g_send_body_size > user_stream->send_body_max ? user_stream->send_body_max : g_send_body_size; + user_stream->send_body = malloc(user_stream->send_body_len); + char *p = user_stream->send_body; + for (int i = 0; i < g_send_body_size; i++) { + *p++ = rand(); + } + + if (user_stream->send_body == NULL) { + printf("send_body malloc error\n"); + return -1; + } + } + + int fin = 1; + if (g_test_case == 304 || g_test_case == 305 + || g_test_case == 311 || g_test_case == 312 + || g_test_case == 313 || g_test_case == 314) { + //do not send fin with data + fin = 0; + // will send fin in a timer + } + + /* send body */ + if (user_stream->send_offset < user_stream->send_body_len) { + ret = xqc_h3_ext_bytestream_send(h3_bs, user_stream->send_body + user_stream->send_offset, user_stream->send_body_len - user_stream->send_offset, fin, XQC_DATA_QOS_HIGH); + + if (ret == -XQC_EAGAIN) { + printf("xqc_h3_ext_bytestream_send eagain %zd\n", ret); + return 0; + + } else if (ret < 0) { + printf("xqc_h3_ext_bytestream_send error %zd\n", ret); + return 0; + + } else { + user_stream->snd_times++; + user_stream->send_offset += ret; + // printf("[bytestream]|send:%"PRIu64"|\n", user_stream->send_offset); + } + } + + return 0; +} + +int xqc_h3_ext_bytestream_create_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *bs_user_data) +{ + user_stream_t *user_stream = (user_stream_t*)bs_user_data; + struct timeval tv; + if (g_test_case == 304 || g_test_case == 305) { + user_stream->ev_bytestream_timer = event_new(eb, -1, 0, xqc_client_bytestream_timeout_callback, user_stream); + tv.tv_sec = 0; + tv.tv_usec = 100000; //100ms + event_add(user_stream->ev_bytestream_timer, &tv); + } + return 0; +} + +int xqc_h3_ext_bytestream_close_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *bs_user_data) +{ + //print stats + xqc_h3_ext_bytestream_stats_t stats = xqc_h3_ext_bytestream_get_stats(h3_ext_bs); + user_stream_t *user_stream = (user_stream_t*)bs_user_data; + + printf("[bytestream]|bytes_sent:%zu|bytes_rcvd:%zu|recv_fin:%d|snd_times:%d|rcv_times:%d|\n", stats.bytes_sent, stats.bytes_rcvd, user_stream->recv_fin, user_stream->snd_times, user_stream->rcv_times); + + //check content + if (g_echo_check) { + if (user_stream->send_body && user_stream->recv_body && !memcmp(user_stream->send_body, user_stream->recv_body, user_stream->send_body_len)) { + printf("[bytestream]|same_content:yes|\n"); + + } else { + printf("[bytestream]|same_content:no|\n"); + } + } + + if (user_stream->send_body) { + free(user_stream->send_body); + } + + if (user_stream->recv_body) { + free(user_stream->recv_body); + } + + if (g_test_case == 304 || g_test_case == 305) { + if (user_stream->ev_bytestream_timer) { + event_free(user_stream->ev_bytestream_timer); + } + } + + free(user_stream); + + return 0; +} + +int xqc_h3_ext_bytestream_read_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + const void *data, size_t data_len, uint8_t fin, void *bs_user_data, uint64_t data_recv_time) +{ + user_stream_t *user_stream = (user_stream_t*)bs_user_data; + int ret = 0; + + if (user_stream->recv_body == NULL) { + user_stream->recv_body = calloc(1, user_stream->send_body_len); + user_stream->recv_body_len = 0; + user_stream->recv_fin = 0; + } + + if (data_len > 0) { + memcpy(user_stream->recv_body + user_stream->recv_body_len, data, data_len); + user_stream->recv_body_len += data_len; + } + + if (!user_stream->recv_fin) { + user_stream->recv_fin = fin; + } + + if (g_test_case == 311 || g_test_case == 312 || g_test_case == 313 || g_test_case == 314) { + user_stream->send_offset = 0; + user_stream->recv_body_len = 0; + user_stream->recv_fin = 0; + user_stream->send_body_len = 1000; + g_test_case = 0; + xqc_client_bytestream_send(h3_ext_bs, user_stream); + } + + user_stream->rcv_times++; + + printf("[bytestream]|stream_id:%"PRIu64"|data_len:%zu|fin:%d|recv_time:%"PRIu64"|\n", xqc_h3_ext_bytestream_id(h3_ext_bs), data_len, fin, data_recv_time); + + return 0; +} + +int xqc_h3_ext_bytestream_write_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *bs_user_data) +{ + user_stream_t *us = bs_user_data; + int ret; + // printf("[bytestream]|write callback|\n"); + ret = xqc_client_bytestream_send(h3_ext_bs, us); + if (ret == -XQC_EAGAIN) { + ret = 0; + printf("[bytestream]|write blocked|\n"); + } + return 0; +} + void xqc_client_request_send_fin_only(int fd, short what, void *arg) { @@ -1492,6 +2240,11 @@ xqc_client_request_send_fin_only(int fd, short what, void *arg) int xqc_client_request_send(xqc_h3_request_t *h3_request, user_stream_t *user_stream) { + if (g_test_case == 99) { + xqc_h3_request_finish(h3_request); + return 0; + } + if (user_stream->start_time == 0) { user_stream->start_time = now(); } @@ -1771,6 +2524,7 @@ xqc_client_request_send(xqc_h3_request_t *h3_request, user_stream_t *user_stream } ret = xqc_h3_request_send_body(h3_request, user_stream->send_body + user_stream->send_offset, user_stream->send_body_len - user_stream->send_offset, fin); if (ret == -XQC_EAGAIN) { + printf("xqc_h3_request_send_body eagain %zd\n", ret); return 0; } else if (ret < 0) { @@ -1845,6 +2599,7 @@ xqc_client_request_write_notify(xqc_h3_request_t *h3_request, void *user_data) return -1; } + printf("request write notify!:%"PRIu64"\n", xqc_h3_stream_id(h3_request)); ret = xqc_client_request_send(h3_request, user_stream); return ret; } @@ -2054,11 +2809,12 @@ xqc_client_request_close_notify(xqc_h3_request_t *h3_request, void *user_data) xqc_request_stats_t stats; stats = xqc_h3_request_get_stats(h3_request); printf("send_body_size:%zu, recv_body_size:%zu, send_header_size:%zu, recv_header_size:%zu, recv_fin:%d, err:%d, " - "mp_state:%d, path0_send_weight:%.2f, path0_recv_weight:%.2f, stream_info:%s\n", + "mp_state:%d, cellular_send_weight:%.2f, cellular_recv_weight:%.2f, stream_info:%s\n", stats.send_body_size, stats.recv_body_size, stats.send_header_size, stats.recv_header_size, user_stream->recv_fin, stats.stream_err, - stats.mp_state, stats.mp_default_path_send_weight, stats.mp_default_path_recv_weight, + stats.mp_state, + stats.mp_standby_path_send_weight, stats.mp_standby_path_recv_weight, stats.stream_info); if (g_echo_check) { @@ -2341,6 +3097,39 @@ xqc_client_abs_timeout_callback(int fd, short what, void *arg) } } +static void +xqc_client_bytestream_timeout_callback(int fd, short what, void *arg) +{ + user_stream_t *user_stream = (user_stream_t *) arg; + int rc = 0; + printf("xqc_client_bytestream_timeout_callback\n"); + if (user_stream->send_offset >= (user_stream->send_body_len)) { + rc = 1; + } + if (g_test_case == 304) { + if (rc == 1) { + printf("xqc_client_bytestream_timeout_callback send fin\n"); + xqc_h3_ext_bytestream_finish(user_stream->h3_ext_bs); + } else { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000; + event_add(user_stream->ev_bytestream_timer, &tv); + } + + } else if (g_test_case == 305) { + if (rc == 1) { + printf("xqc_client_bytestream_timeout_callback close stream\n"); + xqc_h3_ext_bytestream_close(user_stream->h3_ext_bs); + } else { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000; + event_add(user_stream->ev_bytestream_timer, &tv); + } + } +} + static void xqc_client_timeout_multi_process_callback(int fd, short what, void *arg) { user_conn_t *user_conn = (user_conn_t *) arg; @@ -2410,6 +3199,19 @@ xqc_client_timeout_callback(int fd, short what, void *arg) } conn_close: + if (g_send_dgram && g_echo_check) { + if (g_test_case == 206 && g_no_crypt) { + // swap the first & 2nd dgram + printf("%zu %zu\n", dgram1_size, dgram2_size); + if (dgram1_size && dgram2_size) { + //printf("%x %x\n", user_conn->dgram_blk->recv_data[0], user_conn->dgram_blk->recv_data[dgram1_size]); + memcpy(sock_op_buffer, user_conn->dgram_blk->recv_data, dgram1_size); + memmove(user_conn->dgram_blk->recv_data, user_conn->dgram_blk->recv_data + dgram1_size, dgram2_size); + memcpy(user_conn->dgram_blk->recv_data + dgram2_size, sock_op_buffer, dgram1_size); + } + } + printf("[dgram]|echo_check|same_content:%s|\n", !memcmp(user_conn->dgram_blk->data, user_conn->dgram_blk->recv_data, user_conn->dgram_blk->data_len) ? "yes" : "no"); + } printf("xqc_client_timeout_callback | conn_close\n"); rc = xqc_conn_close(ctx.engine, &user_conn->cid); if (rc) { @@ -2479,7 +3281,7 @@ xqc_client_epoch_callback(int fd, short what, void *arg) user_stream->user_conn = user_conn; user_stream->last_recv_log_time = now(); user_stream->recv_log_bytes = 0; - if (user_conn->h3) { + if (user_conn->h3 == 0 || user_conn->h3 == 2) { user_stream->h3_request = xqc_h3_request_create(ctx.engine, &user_conn->cid, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); @@ -2583,7 +3385,7 @@ xqc_client_close_keylog_file(client_ctx_t *ctx) void -xqc_keylog_cb(const char *line, void *user_data) +xqc_keylog_cb(const xqc_cid_t *scid, const char *line, void *user_data) { client_ctx_t *ctx = (client_ctx_t*)user_data; if (ctx->keylog_fd <= 0) { @@ -2591,6 +3393,8 @@ xqc_keylog_cb(const char *line, void *user_data) return; } + printf("scid:%s\n", xqc_scid_str(scid)); + int write_len = write(ctx->keylog_fd, line, strlen(line)); if (write_len < 0) { printf("write keys failed, errno: %d\n", errno); @@ -2647,7 +3451,7 @@ xqc_client_ready_to_create_path(const xqc_cid_t *cid, } ret = xqc_conn_mark_path_available(ctx.engine, &(user_conn->cid), 1); if (ret < 0) { - printf("xqc_conn_mark_path_standby err = %d\n", ret); + printf("xqc_conn_mark_path_available err = %d\n", ret); } } @@ -2733,10 +3537,14 @@ static void xqc_client_concurrent_callback(int fd, short what, void *arg){ conn_ssl_config.transport_parameter_data = NULL; const xqc_cid_t *cid; - if (user_conn->h3) { + if (user_conn->h3 == 0) { cid = xqc_h3_connect(ctx->engine, g_conn_settings, user_conn->token, user_conn->token_len, g_host, g_no_crypt, &conn_ssl_config, user_conn->peer_addr, user_conn->peer_addrlen, user_conn); + } else if (user_conn->h3 == 2) { + cid = xqc_connect(ctx->engine, g_conn_settings, user_conn->token, user_conn->token_len, + g_host, g_no_crypt, &conn_ssl_config, user_conn->peer_addr, + user_conn->peer_addrlen, XQC_DEFINED_ALPN_H3_EXT, user_conn); } else { cid = xqc_connect(ctx->engine, g_conn_settings, user_conn->token, user_conn->token_len, "127.0.0.1", g_no_crypt, &conn_ssl_config, user_conn->peer_addr, @@ -2756,7 +3564,7 @@ static void xqc_client_concurrent_callback(int fd, short what, void *arg){ user_stream_t *user_stream = calloc(1, sizeof(user_stream_t)); user_stream->user_conn = user_conn; - if (user_conn->h3) { + if (user_conn->h3 == 0 || user_conn->h3 == 2) { user_stream->h3_request = xqc_h3_request_create(ctx->engine, cid, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); @@ -2868,7 +3676,7 @@ void usage(int argc, char *argv[]) { " -c Congestion Control Algorithm. r:reno b:bbr c:cubic B:bbr2 bbr+ bbr2+\n" " -C Pacing on.\n" " -t Connection timeout. Default 3 seconds.\n" -" -T Transport layer. No HTTP3.\n" +" -T Transport protocol: 0 H3 (default), 1 Transport layer, 2 H3-ext.\n" " -1 Force 1RTT.\n" " -s Body size to send.\n" " -F Abs_timeout to close conn. >=0.\n" @@ -2904,6 +3712,7 @@ void usage(int argc, char *argv[]) { int main(int argc, char *argv[]) { g_req_cnt = 0; + g_bytestream_cnt = 0; g_req_max = 1; g_send_body_size = 1024*1024; g_send_body_size_defined = 0; @@ -2919,7 +3728,11 @@ int main(int argc, char *argv[]) { g_test_case = 0; g_ipv6 = 0; g_no_crypt = 0; + g_max_dgram_size = 0; + g_send_dgram = 0; g_req_paral = 1; + g_copa_ai = 1.0; + g_copa_delta = 0.05; char server_addr[64] = TEST_SERVER_ADDR; g_server_addr = server_addr; @@ -2935,9 +3748,26 @@ int main(int argc, char *argv[]) { srand(0); //fix the random seed + int long_opt_index; + + const struct option long_opts[] = { + {"copa_delta", required_argument, &long_opt_index, 1}, + {"copa_ai_unit", required_argument, &long_opt_index, 2}, + {0, 0, 0, 0} + }; + int ch = 0; - while ((ch = getopt(argc, argv, "a:p:P:n:c:Ct:T1s:w:r:l:Ed:u:H:h:Gx:6NMR:i:V:q:o:fe:F:D:b:B:J:QA")) != -1) { + while ((ch = getopt_long(argc, argv, "a:p:P:n:c:Ct:T:1s:w:r:l:Ed:u:H:h:Gx:6NMR:i:V:q:o:fe:F:D:b:B:J:Q:U:Ay", long_opts, NULL)) != -1) { switch (ch) { + case 'U': + printf("option send_datagram 0 (off), 1 (on), 2(on + batch): %s\n", optarg); + g_send_dgram = atoi(optarg); + break; + case 'Q': + /* max_datagram_frame_size */ + printf("option max_datagram_frame_size: %s\n", optarg); + g_max_dgram_size = atoi(optarg); + break; case 'a': /* Server addr. */ printf("option addr :%s\n", optarg); snprintf(server_addr, sizeof(server_addr), optarg); @@ -2978,8 +3808,8 @@ int main(int argc, char *argv[]) { break; case 'T': /* Transport layer. No HTTP3. */ printf("option transport :%s\n", "on"); - transport = 1; - g_transport = 1; + transport = atoi(optarg); + g_transport = transport; break; case '1': /* Force 1RTT. */ printf("option 1RTT :%s\n", "on"); @@ -3118,7 +3948,7 @@ int main(int argc, char *argv[]) { printf("random cid:%s\n", optarg); g_random_cid = atoi(optarg); break; - case 'Q': + case 'y': printf("option multipath backup path standby :%s\n", "on"); g_mp_backup_mode = 1; break; @@ -3126,6 +3956,37 @@ int main(int argc, char *argv[]) { printf("option multipath request accelerate :%s\n", "on"); g_mp_request_accelerate = 1; break; + /* long options */ + case 0: + + switch (long_opt_index) + { + case 1: /* copa_delta */ + g_copa_delta = atof(optarg); + if (g_copa_delta <= 0 || g_copa_delta > 0.5) { + printf("option g_copa_delta must be in (0, 0.5]\n"); + exit(0); + } else { + printf("option g_copa_delta: %.4lf\n", g_copa_delta); + } + break; + + case 2: /* copa_ai_unit */ + + g_copa_ai = atof(optarg); + if (g_copa_ai < 1.0) { + printf("option g_copa_ai must be greater than 1.0\n"); + exit(0); + } else { + printf("option g_copa_ai: %.4lf\n", g_copa_ai); + } + break; + + default: + break; + } + + break; default: printf("other option :%c\n", ch); @@ -3139,6 +4000,10 @@ int main(int argc, char *argv[]) { memset(g_header_value, 'v', sizeof(g_header_value)); memset(&ctx, 0, sizeof(ctx)); + if (g_test_case == 44) { + xqc_log_enable(XQC_FALSE); + } + xqc_client_open_keylog_file(&ctx); xqc_client_open_log_file(&ctx); @@ -3205,8 +4070,14 @@ int main(int argc, char *argv[]) { #endif } #endif - else { - printf("unknown cong_ctrl, option is b, r, c, B, bbr+, bbr2+\n"); + else if (c_cong_ctl == 'u') { + cong_ctrl = xqc_unlimited_cc_cb; + + } else if (c_cong_ctl == 'P') { + cong_ctrl = xqc_copa_cb; + + } else { + printf("unknown cong_ctrl, option is b, r, c, B, bbr+, bbr2+, u\n"); return -1; } printf("congestion control flags: %x\n", cong_flags); @@ -3219,13 +4090,34 @@ int main(int argc, char *argv[]) { .customize_on = 1, .init_cwnd = 32, .cc_optimization_flags = cong_flags, + .copa_delta_ai_unit = g_copa_ai, + .copa_delta_base = g_copa_delta, }, - //.so_sndbuf = 1024*1024, - .proto_version = XQC_VERSION_V1, .spurious_loss_detect_on = 0, .keyupdate_pkt_threshold = 0, + .max_datagram_frame_size = g_max_dgram_size, .enable_multipath = g_enable_multipath, }; + + + if (g_test_case == 400) { + //low_delay + conn_settings = xqc_conn_get_conn_settings_template(XQC_CONN_SETTINGS_LOW_DELAY); + } + + conn_settings.pacing_on = pacing_on; + conn_settings.proto_version = XQC_VERSION_V1; + conn_settings.max_datagram_frame_size = g_max_dgram_size; + conn_settings.enable_multipath = g_enable_multipath; + + if (g_test_case == 208) { + conn_settings.datagram_redundancy = 1; + } + + if (g_test_case == 209) { + conn_settings.datagram_redundant_probe = 30000; + } + g_conn_settings = &conn_settings; xqc_config_t config; @@ -3233,6 +4125,11 @@ int main(int argc, char *argv[]) { return -1; } + + if (transport == 2 && g_test_case != 315) { + config.enable_h3_ext = 1; + } + switch(c_log_level) { case 'e': config.cfg_log_level = XQC_LOG_ERROR; break; case 'i': config.cfg_log_level = XQC_LOG_INFO; break; @@ -3279,6 +4176,10 @@ int main(int argc, char *argv[]) { conn_settings.max_pkt_out_size = 1400; } + if (g_test_case == 201) { + conn_settings.max_pkt_out_size = 1216; + } + if (g_test_qch_mode) { pid_t pid; int i; @@ -3329,7 +4230,19 @@ int main(int argc, char *argv[]) { .h3_request_close_notify = xqc_client_request_close_notify, .h3_request_read_notify = xqc_client_request_read_notify, .h3_request_write_notify = xqc_client_request_write_notify, - } + }, + .h3_ext_dgram_cbs = { + .dgram_read_notify = xqc_client_h3_ext_datagram_read_callback, + .dgram_write_notify = xqc_client_h3_ext_datagram_write_callback, + .dgram_acked_notify = xqc_client_h3_ext_datagram_acked_callback, + .dgram_lost_notify = xqc_client_h3_ext_datagram_lost_callback, + }, + .h3_ext_bs_cbs = { + .bs_read_notify = xqc_h3_ext_bytestream_read_callback, + .bs_write_notify = xqc_h3_ext_bytestream_write_callback, + .bs_create_notify = xqc_h3_ext_bytestream_create_callback, + .bs_close_notify = xqc_h3_ext_bytestream_close_callback, + }, }; /* init http3 context */ @@ -3351,6 +4264,12 @@ int main(int argc, char *argv[]) { .stream_write_notify = xqc_client_stream_write_notify, .stream_read_notify = xqc_client_stream_read_notify, .stream_close_notify = xqc_client_stream_close_notify, + }, + .dgram_cbs = { + .datagram_write_notify = xqc_client_datagram_write_callback, + .datagram_read_notify = xqc_client_datagram_read_callback, + .datagram_acked_notify = xqc_client_datagram_acked_callback, + .datagram_lost_notify = xqc_client_datagram_lost_callback, } }; @@ -3440,11 +4359,18 @@ int main(int argc, char *argv[]) { const xqc_cid_t *cid; - if (user_conn->h3) { + + printf("conn type: %d\n", user_conn->h3); + + if (user_conn->h3 == 0) { if (g_test_case == 7) {user_conn->token_len = -1;} /* create connection fail */ cid = xqc_h3_connect(ctx.engine, &conn_settings, user_conn->token, user_conn->token_len, g_host, g_no_crypt, &conn_ssl_config, user_conn->peer_addr, user_conn->peer_addrlen, user_conn); + } else if (user_conn->h3 == 2) { + cid = xqc_connect(ctx.engine, &conn_settings, user_conn->token, user_conn->token_len, + g_host, g_no_crypt, &conn_ssl_config, user_conn->peer_addr, + user_conn->peer_addrlen, XQC_DEFINED_ALPN_H3_EXT, user_conn); } else { if (g_test_case == 43) { /* try a alpn not supported by server */ @@ -3468,14 +4394,85 @@ int main(int argc, char *argv[]) { /* copy cid to its own memory space to prevent crashes caused by internal cid being freed */ memcpy(&user_conn->cid, cid, sizeof(*cid)); - if (g_test_case != 500) { + user_conn->dgram_blk = calloc(1, sizeof(user_dgram_blk_t)); + user_conn->dgram_blk->data_sent = 0; + user_conn->dgram_blk->data_recv = 0; + if (user_conn->quic_conn) { + printf("[dgram]|prepare_dgram_user_data|\n"); + xqc_datagram_set_user_data(user_conn->quic_conn, user_conn); + + } + + if (user_conn->h3_conn) { + printf("[h3-dgram]|prepare_dgram_user_data|\n"); + xqc_h3_ext_datagram_set_user_data(user_conn->h3_conn, user_conn); + + } + + if (g_test_case >= 300 && g_test_case < 400 && user_conn->h3 == 2) { + // for h3 bytestream testcases + // send h3 requests + if (g_test_case != 306 && g_test_case != 307 && g_test_case != 308 && g_test_case != 310 + && g_test_case != 311 && g_test_case != 312 && g_test_case != 313) + { + for (int i = 0; i < g_req_paral; i++) { + g_req_cnt++; + user_stream_t *user_stream = calloc(1, sizeof(user_stream_t)); + user_stream->user_conn = user_conn; + user_stream->last_recv_log_time = now(); + user_stream->recv_log_bytes = 0; + user_stream->h3_request = xqc_h3_request_create(ctx.engine, cid, user_stream); + if (user_stream->h3_request == NULL) { + printf("xqc_h3_request_create error\n"); + continue; + } + + xqc_client_request_send(user_stream->h3_request, user_stream); + + } + } + + // open bytestreams and send data + for (int i = 0; i < g_req_paral; i++) { + g_bytestream_cnt++; + user_stream_t *user_stream = calloc(1, sizeof(user_stream_t)); + user_stream->user_conn = user_conn; + user_stream->last_recv_log_time = now(); + user_stream->recv_log_bytes = 0; + user_stream->h3_ext_bs = xqc_h3_ext_bytestream_create(ctx.engine, cid, user_stream); + if (user_stream->h3_ext_bs == NULL) { + printf("xqc_h3_ext_bytestream_create error\n"); + continue; + } + + xqc_client_bytestream_send(user_stream->h3_ext_bs, user_stream); + + } + + // prepare to send datagrams + if (g_test_case != 306 && g_test_case != 307 && g_test_case != 308 && g_test_case != 310 + && g_test_case != 311 && g_test_case != 312 && g_test_case != 313) + { + if (g_send_dgram) { + user_conn->dgram_blk->data = calloc(1, g_send_body_size); + user_conn->dgram_blk->data[0] = 0x1; + user_conn->dgram_blk->data_len = g_send_body_size; + if (g_echo_check) { + user_conn->dgram_blk->recv_data = calloc(1, g_send_body_size); + user_conn->dgram_blk->recv_data[0] = 0x2; + } + xqc_client_h3_ext_datagram_send(user_conn); + } + } + + } else if (!g_send_dgram && g_test_case != 500) { for (int i = 0; i < g_req_paral; i++) { g_req_cnt++; user_stream_t *user_stream = calloc(1, sizeof(user_stream_t)); user_stream->user_conn = user_conn; user_stream->last_recv_log_time = now(); user_stream->recv_log_bytes = 0; - if (user_conn->h3) { + if (user_conn->h3 == 0 || user_conn->h3 == 2) { if (g_test_case == 11) { /* create stream fail */ xqc_cid_t tmp; xqc_h3_request_create(ctx.engine, &tmp, user_stream); @@ -3502,6 +4499,20 @@ int main(int argc, char *argv[]) { } last_recv_ts = now(); + + } else { + user_conn->dgram_blk->data = calloc(1, g_send_body_size); + user_conn->dgram_blk->data_len = g_send_body_size; + if (g_echo_check) { + user_conn->dgram_blk->recv_data = calloc(1, g_send_body_size); + } + if (user_conn->h3 == 2) { + xqc_client_h3_ext_datagram_send(user_conn); + + } else if (user_conn->h3 == 1) { + xqc_client_datagram_send(user_conn); + } + } event_base_dispatch(eb); @@ -3513,6 +4524,16 @@ int main(int argc, char *argv[]) { } event_free(user_conn->ev_timeout); + if (user_conn->dgram_blk) { + if (user_conn->dgram_blk->data) { + free(user_conn->dgram_blk->data); + } + if (user_conn->dgram_blk->recv_data) { + free(user_conn->dgram_blk->recv_data); + } + free(user_conn->dgram_blk); + } + free(user_conn->peer_addr); free(user_conn->local_addr); free(user_conn); diff --git a/tests/test_server.c b/tests/test_server.c index 0632740d9..074d6a30f 100644 --- a/tests/test_server.c +++ b/tests/test_server.c @@ -16,6 +16,7 @@ #include #include #include +#include #define XQC_FIRST_OCTET 1 int @@ -37,6 +38,18 @@ printf_null(const char *format, ...) #define XQC_MAX_LOG_LEN 2048 +#define XQC_TEST_DGRAM_BATCH_SZ 32 + +typedef struct user_datagram_block_s { + unsigned char *data; + size_t data_len; + size_t to_send_size; + size_t data_sent; + size_t data_recv; + size_t data_lost; + size_t dgram_lost; +} user_dgram_blk_t; + typedef struct xqc_quic_lb_ctx_s { uint8_t sid_len; @@ -50,17 +63,23 @@ typedef struct xqc_quic_lb_ctx_s { typedef struct user_stream_s { - xqc_stream_t *stream; - xqc_h3_request_t *h3_request; - uint64_t send_offset; - int header_sent; - int header_recvd; - char *send_body; - size_t send_body_len; - size_t send_body_max; - char *recv_body; - size_t recv_body_len; - FILE *recv_body_fp; + xqc_stream_t *stream; + xqc_h3_request_t *h3_request; + uint64_t send_offset; + int header_sent; + int header_recvd; + char *send_body; + size_t send_body_len; + size_t send_body_max; + char *recv_body; + size_t recv_body_len; + FILE *recv_body_fp; + xqc_h3_ext_bytestream_t *h3_ext_bs; + int recv_fin; + int echo_fin; + + int snd_times; + int rcv_times; } user_stream_t; typedef struct user_conn_s { @@ -68,6 +87,13 @@ typedef struct user_conn_s { struct sockaddr_in6 peer_addr; socklen_t peer_addrlen; xqc_cid_t cid; + + user_dgram_blk_t *dgram_blk; + size_t dgram_mss; + uint8_t dgram_not_supported; + + xqc_connection_t *quic_conn; + xqc_h3_conn_t *h3_conn; } user_conn_t; typedef struct xqc_server_ctx_s { @@ -89,6 +115,8 @@ typedef struct { xqc_server_ctx_t ctx; struct event_base *eb; +int g_send_dgram; +int g_max_dgram_size; int g_send_body_size_from_cdf; cdf_entry_t *cdf_list; int cdf_list_size; @@ -98,6 +126,7 @@ int g_send_body_size_defined; int g_save_body; int g_read_body; int g_spec_url; +//99 pure fin int g_test_case; int g_ipv6; int g_batch=0; @@ -107,6 +136,9 @@ int g_enable_reinjection = 0; int g_spec_local_addr = 0; int g_mpshell = 0; int g_endless_sending = 0; +double g_copa_ai = 1.0; +double g_copa_delta = 0.05; +int g_enable_h3_ext = 1; int g_mp_backup_mode = 0; char g_write_file[256]; char g_read_file[256]; @@ -125,6 +157,313 @@ static uint64_t last_snd_ts; #define XQC_TEST_LONG_HEADER_LEN 32769 char test_long_value[XQC_TEST_LONG_HEADER_LEN] = {'\0'}; +static void +xqc_server_datagram_send(user_conn_t *user_conn) +{ + if (user_conn->dgram_not_supported) { + // exit + printf("[dgram]|peer_does_not_support_datagram|\n"); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } + + user_dgram_blk_t *dgram_blk = user_conn->dgram_blk; + int ret; + + if (g_send_dgram == 1) { + uint64_t dgram_id; + while (dgram_blk->data_sent < dgram_blk->to_send_size) { + size_t dgram_size = dgram_blk->to_send_size - dgram_blk->data_sent; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + dgram_blk->data[dgram_blk->data_sent] = 0x31; + ret = xqc_datagram_send(user_conn->quic_conn, dgram_blk->data + dgram_blk->data_sent, dgram_size, &dgram_id, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EAGAIN) { + printf("[dgram]|retry_datagram_send_later|\n"); + return; + } else if (ret == -XQC_EDGRAM_TOO_LARGE ) { + printf("[dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, dgram_size, xqc_datagram_get_mss(user_conn->quic_conn)); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0) { + printf("[dgram]|send_datagram_error|err_code:%d|\n", ret); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } + //printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id, dgram_size); + dgram_blk->data_sent += dgram_size; + } + } else if (g_send_dgram == 2) { + struct iovec iov[XQC_TEST_DGRAM_BATCH_SZ]; + uint64_t dgram_id_list[XQC_TEST_DGRAM_BATCH_SZ]; + size_t bytes_in_batch = 0; + int batch_cnt = 0; + while ((dgram_blk->data_sent + bytes_in_batch) < dgram_blk->to_send_size) { + size_t dgram_size = dgram_blk->to_send_size - dgram_blk->data_sent - bytes_in_batch; + size_t succ_sent = 0, succ_sent_bytes = 0; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + iov[batch_cnt].iov_base = dgram_blk->data + dgram_blk->data_sent + bytes_in_batch; + iov[batch_cnt].iov_len = dgram_size; + dgram_blk->data[dgram_blk->data_sent + bytes_in_batch] = 0x31; + bytes_in_batch += dgram_size; + batch_cnt++; + if ((bytes_in_batch + dgram_blk->data_sent) == dgram_blk->to_send_size + || batch_cnt == XQC_TEST_DGRAM_BATCH_SZ) + { + ret = xqc_datagram_send_multiple(user_conn->quic_conn, iov, dgram_id_list, batch_cnt, &succ_sent, &succ_sent_bytes, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EDGRAM_TOO_LARGE) { + printf("[dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, iov[succ_sent].iov_len, xqc_datagram_get_mss(user_conn->quic_conn)); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0 && ret != -XQC_EAGAIN) { + printf("[dgram]|send_datagram_multiple_error|err_code:%d|\n", ret); + xqc_conn_close(ctx.engine, &user_conn->cid); + return; + } + + // for (int i = 0; i < succ_sent; i++) { + // printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id_list[i], iov[i].iov_len); + // } + + // printf("[dgram]|datagrams_sent_in_a_batch|cnt:%zu|size:%zu|\n", succ_sent, succ_sent_bytes); + + dgram_blk->data_sent += succ_sent_bytes; + + if (ret == -XQC_EAGAIN) { + printf("[dgram]|retry_datagram_send_multiple_later|\n"); + return; + } + + bytes_in_batch = 0; + batch_cnt = 0; + } + } + + } +} + +static void +xqc_server_datagram_read_callback(xqc_connection_t *conn, void *user_data, const void *data, size_t data_len, uint64_t dgram_ts) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + + if (!user_conn->dgram_not_supported && user_conn->dgram_mss == 0) { + user_conn->dgram_mss = xqc_datagram_get_mss(conn); + user_conn->dgram_not_supported = user_conn->dgram_mss == 0; + if (g_test_case == 200 || g_test_case == 201) { + printf("[dgram-200]|1RTT|initial_mss:%zu|\n", user_conn->dgram_mss); + } + } + + if (g_echo && g_send_dgram) { + uint64_t dgram_id; + int ret; + if (user_conn->dgram_blk->data_recv + data_len > user_conn->dgram_blk->data_len) { + //expand buffer size + size_t new_len = (user_conn->dgram_blk->data_recv + data_len) << 1; + unsigned char *new_data = calloc(1, new_len); + memcpy(new_data, user_conn->dgram_blk->data, user_conn->dgram_blk->data_recv); + if (user_conn->dgram_blk->data) { + free(user_conn->dgram_blk->data); + } + user_conn->dgram_blk->data = new_data; + user_conn->dgram_blk->data_len = new_len; + } + memcpy(user_conn->dgram_blk->data + user_conn->dgram_blk->data_recv, data, data_len); + user_conn->dgram_blk->data_recv += data_len; + user_conn->dgram_blk->to_send_size = user_conn->dgram_blk->data_recv; + + } + + if (g_send_dgram){ + if (user_conn->dgram_blk->data_sent < user_conn->dgram_blk->data_len) { + xqc_server_datagram_send(user_conn); + } + } +} + +static void +xqc_server_datagram_write_callback(xqc_connection_t *conn, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + if (g_send_dgram) { + xqc_server_datagram_send(user_conn); + } +} + +static void +xqc_server_datagram_acked_callback(xqc_connection_t *conn, uint64_t dgram_id, void *user_data) +{ + +} + +static int +xqc_server_datagram_lost_callback(xqc_connection_t *conn, uint64_t dgram_id, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + //user_conn->dgram_blk->data_lost += data_len; + user_conn->dgram_blk->dgram_lost++; + return 0; +} + + +static void +xqc_server_h3_ext_datagram_send(user_conn_t *user_conn) +{ + if (user_conn->dgram_not_supported) { + // exit + printf("[h3-dgram]|peer_does_not_support_datagram|\n"); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } + + user_dgram_blk_t *dgram_blk = user_conn->dgram_blk; + int ret; + + if (g_send_dgram == 1) { + uint64_t dgram_id; + while (dgram_blk->data_sent < dgram_blk->to_send_size) { + size_t dgram_size = dgram_blk->to_send_size - dgram_blk->data_sent; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + dgram_blk->data[dgram_blk->data_sent] = 0x31; + ret = xqc_h3_ext_datagram_send(user_conn->h3_conn, dgram_blk->data + dgram_blk->data_sent, dgram_size, &dgram_id, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EAGAIN) { + printf("[h3-dgram]|retry_datagram_send_later|\n"); + return; + } else if (ret == -XQC_EDGRAM_TOO_LARGE ) { + printf("[h3-dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, dgram_size, xqc_h3_ext_datagram_get_mss(user_conn->h3_conn)); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0) { + printf("[h3-dgram]|send_datagram_error|err_code:%d|\n", ret); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } + //printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id, dgram_size); + dgram_blk->data_sent += dgram_size; + } + } else if (g_send_dgram == 2) { + struct iovec iov[XQC_TEST_DGRAM_BATCH_SZ]; + uint64_t dgram_id_list[XQC_TEST_DGRAM_BATCH_SZ]; + size_t bytes_in_batch = 0; + int batch_cnt = 0; + while ((dgram_blk->data_sent + bytes_in_batch) < dgram_blk->to_send_size) { + size_t dgram_size = dgram_blk->to_send_size - dgram_blk->data_sent - bytes_in_batch; + size_t succ_sent = 0, succ_sent_bytes = 0; + if (dgram_size > user_conn->dgram_mss) { + dgram_size = user_conn->dgram_mss; + } + iov[batch_cnt].iov_base = dgram_blk->data + dgram_blk->data_sent + bytes_in_batch; + iov[batch_cnt].iov_len = dgram_size; + dgram_blk->data[dgram_blk->data_sent + bytes_in_batch] = 0x31; + bytes_in_batch += dgram_size; + batch_cnt++; + if ((bytes_in_batch + dgram_blk->data_sent) == dgram_blk->to_send_size + || batch_cnt == XQC_TEST_DGRAM_BATCH_SZ) + { + ret = xqc_h3_ext_datagram_send_multiple(user_conn->h3_conn, iov, dgram_id_list, batch_cnt, &succ_sent, &succ_sent_bytes, XQC_DATA_QOS_HIGH); + if (ret == -XQC_EDGRAM_TOO_LARGE) { + printf("[h3-dgram]|trying_to_send_an_oversized_datagram|recorded_mss:%zu|send_size:%zu|current_mss:%zu|\n", user_conn->dgram_mss, iov[succ_sent].iov_len, xqc_h3_ext_datagram_get_mss(user_conn->h3_conn)); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } else if (ret < 0 && ret != -XQC_EAGAIN) { + printf("[h3-dgram]|send_datagram_multiple_error|err_code:%d|\n", ret); + xqc_h3_conn_close(ctx.engine, &user_conn->cid); + return; + } + + // for (int i = 0; i < succ_sent; i++) { + // printf("[dgram]|send_one_datagram|id:%"PRIu64"|size:%zu|\n", dgram_id_list[i], iov[i].iov_len); + // } + + // printf("[dgram]|datagrams_sent_in_a_batch|cnt:%zu|size:%zu|\n", succ_sent, succ_sent_bytes); + + dgram_blk->data_sent += succ_sent_bytes; + + if (ret == -XQC_EAGAIN) { + printf("[h3-dgram]|retry_datagram_send_multiple_later|\n"); + return; + } + + bytes_in_batch = 0; + batch_cnt = 0; + } + } + + } +} + +static void +xqc_server_h3_ext_datagram_read_callback(xqc_h3_conn_t *conn, const void *data, size_t data_len, void *user_data, uint64_t ts) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + + if (!user_conn->dgram_not_supported && user_conn->dgram_mss == 0) { + user_conn->dgram_mss = xqc_h3_ext_datagram_get_mss(conn); + user_conn->dgram_not_supported = user_conn->dgram_mss == 0; + if (g_test_case == 200 || g_test_case == 201) { + printf("[h3-dgram-200]|1RTT|initial_mss:%zu|\n", user_conn->dgram_mss); + } + } + + if (g_echo && g_send_dgram) { + uint64_t dgram_id; + int ret; + if (user_conn->dgram_blk->data_recv + data_len > user_conn->dgram_blk->data_len) { + //expand buffer size + size_t new_len = (user_conn->dgram_blk->data_recv + data_len) << 1; + unsigned char *new_data = calloc(1, new_len); + memcpy(new_data, user_conn->dgram_blk->data, user_conn->dgram_blk->data_recv); + if (user_conn->dgram_blk->data) { + free(user_conn->dgram_blk->data); + } + user_conn->dgram_blk->data = new_data; + user_conn->dgram_blk->data_len = new_len; + } + memcpy(user_conn->dgram_blk->data + user_conn->dgram_blk->data_recv, data, data_len); + user_conn->dgram_blk->data_recv += data_len; + user_conn->dgram_blk->to_send_size = user_conn->dgram_blk->data_recv; + } + + // printf("recv:%zd, to_send:%zd, data_len: %zd, sent: %zd\n", user_conn->dgram_blk->data_recv, user_conn->dgram_blk->to_send_size, user_conn->dgram_blk->data_len, user_conn->dgram_blk->data_sent); + + if (g_send_dgram){ + if (user_conn->dgram_blk->data_sent < user_conn->dgram_blk->to_send_size) { + xqc_server_h3_ext_datagram_send(user_conn); + } + } +} + +static void +xqc_server_h3_ext_datagram_write_callback(xqc_h3_conn_t *conn, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + printf("h3 datagram write notify!\n"); + if (g_send_dgram) { + xqc_server_h3_ext_datagram_send(user_conn); + } +} + +static void +xqc_server_h3_ext_datagram_acked_callback(xqc_h3_conn_t *conn, uint64_t dgram_id, void *user_data) +{ + +} + +static int +xqc_server_h3_ext_datagram_lost_callback(xqc_h3_conn_t *conn, uint64_t dgram_id, void *user_data) +{ + user_conn_t *user_conn = (user_conn_t*)user_data; + user_conn->dgram_blk->data_lost += 0; + user_conn->dgram_blk->dgram_lost++; + return 0; +} + /* CDF file format: N (N lines) @@ -255,6 +594,23 @@ int xqc_server_conn_create_notify(xqc_connection_t *conn, const xqc_cid_t *cid, void *user_data, void *conn_proto_data) { DEBUG; + user_conn_t *user_conn = (user_conn_t*)user_data; + + user_conn->quic_conn = conn; + user_conn->dgram_blk = calloc(1, sizeof(user_dgram_blk_t)); + user_conn->dgram_blk->data_recv = 0; + user_conn->dgram_blk->data_sent = 0; + + xqc_datagram_set_user_data(conn, user_conn); + + if (g_send_dgram) { + user_conn->dgram_blk->data = calloc(1, g_send_body_size); + user_conn->dgram_blk->data_len = g_send_body_size; + if (!g_echo) { + user_conn->dgram_blk->to_send_size = g_send_body_size; + } + } + return 0; } @@ -262,10 +618,21 @@ int xqc_server_conn_close_notify(xqc_connection_t *conn, const xqc_cid_t *cid, void *user_data, void *conn_proto_data) { DEBUG; - user_conn_t *user_conn = (user_conn_t *)conn_proto_data; + user_conn_t *user_conn = (user_conn_t *)user_data; xqc_conn_stats_t stats = xqc_conn_get_stats(ctx.engine, cid); - printf("send_count:%u, lost_count:%u, tlp_count:%u, recv_count:%u, srtt:%"PRIu64" early_data_flag:%d, conn_err:%d, ack_info:%s\n", - stats.send_count, stats.lost_count, stats.tlp_count, stats.recv_count, stats.srtt, stats.early_data_flag, stats.conn_err, stats.ack_info); + printf("send_count:%u, lost_count:%u, lost_dgram_count:%u, tlp_count:%u, recv_count:%u, srtt:%"PRIu64" early_data_flag:%d, conn_err:%d, ack_info:%s\n", + stats.send_count, stats.lost_count, stats.lost_dgram_count, stats.tlp_count, stats.recv_count, stats.srtt, stats.early_data_flag, stats.conn_err, stats.ack_info); + + printf("[dgram]|recv_dgram_bytes:%zu|sent_dgram_bytes:%zu|lost_dgram_bytes:%zu|lost_cnt:%zu|\n", + user_conn->dgram_blk->data_recv, user_conn->dgram_blk->data_sent, + user_conn->dgram_blk->data_lost, user_conn->dgram_blk->dgram_lost); + + if (user_conn->dgram_blk) { + if (user_conn->dgram_blk->data) { + free(user_conn->dgram_blk->data); + } + free(user_conn->dgram_blk); + } free(user_conn); @@ -282,6 +649,7 @@ xqc_server_conn_handshake_finished(xqc_connection_t *conn, void *user_data, void { DEBUG; user_conn_t *user_conn = (user_conn_t *) user_data; + printf("datagram_mss:%zd\n", xqc_datagram_get_mss(conn)); } void @@ -385,6 +753,10 @@ xqc_server_stream_create_notify(xqc_stream_t *stream, void *user_data) user_stream->stream = stream; xqc_stream_set_user_data(stream, user_stream); + if (g_test_case == 99) { + xqc_stream_send(stream, NULL, 0, 1); + } + return 0; } @@ -482,6 +854,22 @@ xqc_server_h3_conn_create_notify(xqc_h3_conn_t *h3_conn, const xqc_cid_t *cid, v /* user_conn_t *user_conn = (xqc_server_ctx_t*)conn_user_data; */ user_conn_t *user_conn = calloc(1, sizeof(user_conn_t)); + + user_conn->h3_conn = h3_conn; + user_conn->dgram_blk = calloc(1, sizeof(user_dgram_blk_t)); + user_conn->dgram_blk->data_recv = 0; + user_conn->dgram_blk->data_sent = 0; + + xqc_h3_ext_datagram_set_user_data(h3_conn, user_conn); + + if (g_send_dgram) { + user_conn->dgram_blk->data = calloc(1, g_send_body_size); + user_conn->dgram_blk->data_len = g_send_body_size; + if (!g_echo) { + user_conn->dgram_blk->to_send_size = user_conn->dgram_blk->data_len; + } + } + xqc_h3_conn_set_user_data(h3_conn, user_conn); xqc_h3_conn_get_peer_addr(h3_conn, (struct sockaddr *)&user_conn->peer_addr, @@ -501,6 +889,17 @@ xqc_server_h3_conn_close_notify(xqc_h3_conn_t *h3_conn, const xqc_cid_t *cid, vo printf("send_count:%u, lost_count:%u, tlp_count:%u, recv_count:%u, srtt:%"PRIu64" early_data_flag:%d, conn_err:%d, ack_info:%s, conn_info:%s\n", stats.send_count, stats.lost_count, stats.tlp_count, stats.recv_count, stats.srtt, stats.early_data_flag, stats.conn_err, stats.ack_info, stats.conn_info); + printf("[h3-dgram]|recv_dgram_bytes:%zu|sent_dgram_bytes:%zu|lost_dgram_bytes:%zu|lost_cnt:%zu|\n", + user_conn->dgram_blk->data_recv, user_conn->dgram_blk->data_sent, + user_conn->dgram_blk->data_lost, user_conn->dgram_blk->dgram_lost); + + if (user_conn->dgram_blk) { + if (user_conn->dgram_blk->data) { + free(user_conn->dgram_blk->data); + } + free(user_conn->dgram_blk); + } + free(user_conn); if (g_mpshell) { @@ -518,6 +917,7 @@ xqc_server_h3_conn_handshake_finished(xqc_h3_conn_t *h3_conn, void *conn_user_da user_conn_t *user_conn = (user_conn_t *)conn_user_data; xqc_conn_stats_t stats = xqc_conn_get_stats(ctx.engine, &user_conn->cid); printf("0rtt_flag:%d\n", stats.early_data_flag); + printf("h3_datagram_mss:%zd\n", xqc_h3_ext_datagram_get_mss(h3_conn)); } void @@ -536,6 +936,131 @@ xqc_server_h3_conn_update_cid_notify(xqc_h3_conn_t *h3_conn, const xqc_cid_t *re } +int +xqc_server_bytestream_send(xqc_h3_ext_bytestream_t *h3_bs, user_stream_t *user_stream) +{ + int ret = 0; + /* echo bytestream */ + if (user_stream->send_offset < user_stream->recv_body_len || (!user_stream->echo_fin && user_stream->recv_fin)) { + ret = xqc_h3_ext_bytestream_send(h3_bs, user_stream->send_body + user_stream->send_offset, user_stream->recv_body_len - user_stream->send_offset, user_stream->recv_fin, XQC_DATA_QOS_HIGH); + + if (ret == -XQC_EAGAIN) { + return ret; + + } else if (ret < 0) { + printf("xqc_h3_ext_bytestream_send error %d\n", ret); + return ret; + + } else { + user_stream->snd_times++; + user_stream->send_offset += ret; + if (user_stream->recv_fin && user_stream->send_offset == user_stream->recv_body_len) { + user_stream->echo_fin = 1; + } + } + } + + return 0; +} + +int xqc_h3_ext_bytestream_create_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *bs_user_data) +{ + user_stream_t *user_stream = calloc(1, sizeof(user_stream_t)); + user_stream->h3_ext_bs = h3_ext_bs; + xqc_h3_ext_bytestream_set_user_data(h3_ext_bs, user_stream); + + if (user_stream->send_body == NULL) { + user_stream->send_body_max = MAX_BUF_SIZE; + user_stream->send_body_len = user_stream->send_body_max; + user_stream->send_body = malloc(user_stream->send_body_len); + user_stream->send_offset = 0; + user_stream->recv_body_len = 0; + user_stream->recv_fin = 0; + } + + if (g_test_case == 99) { + xqc_h3_ext_bytestream_finish(h3_ext_bs); + } + + printf("[bytestream]| stream: %"PRIu64" create callback|\n", xqc_h3_ext_bytestream_id(h3_ext_bs)); + + return 0; +} + +int xqc_h3_ext_bytestream_close_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *bs_user_data) +{ + //print stats + xqc_h3_ext_bytestream_stats_t stats = xqc_h3_ext_bytestream_get_stats(h3_ext_bs); + user_stream_t *user_stream = (user_stream_t*)bs_user_data; + + printf("[bytestream]|bytes_sent:%zu|bytes_rcvd:%zu|recv_fin:%d|snd_times:%d|rcv_times:%d|\n", stats.bytes_sent, stats.bytes_rcvd, user_stream->recv_fin, user_stream->snd_times, user_stream->rcv_times); + + if (user_stream->send_body) { + free(user_stream->send_body); + } + + if (user_stream->recv_body) { + free(user_stream->recv_body); + } + + free(user_stream); + return 0; +} + +int xqc_h3_ext_bytestream_read_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + const void *data, size_t data_len, uint8_t fin, void *bs_user_data, uint64_t data_recv_time) +{ + user_stream_t *user_stream = (user_stream_t*)bs_user_data; + int ret = 0, sent = 0; + + user_stream->recv_body_len = 0; + user_stream->recv_fin = 0; + user_stream->send_offset = 0; + + if (data_len > 0) { + memcpy(user_stream->send_body + user_stream->recv_body_len, data, data_len); + user_stream->recv_body_len += data_len; + } + + if (!user_stream->recv_fin) { + user_stream->recv_fin = fin; + } + + user_stream->rcv_times++; + + printf("[bytestream]|stream_id:%"PRIu64"|data_len:%zu|fin:%d|recv_time:%"PRIu64"|\n", + xqc_h3_ext_bytestream_id(h3_ext_bs), data_len, fin, data_recv_time); + + sent = xqc_server_bytestream_send(h3_ext_bs, user_stream); + if (sent < 0 && sent != -XQC_EAGAIN) { + //something went wrong + printf("xqc_server_bytestream_send error: %d\n", ret); + if (!(sent == -XQC_H3_BYTESTREAM_FIN_SENT && g_test_case == 99)) { + xqc_h3_ext_bytestream_close(h3_ext_bs); + } + } + + return 0; +} + +int xqc_h3_ext_bytestream_write_callback(xqc_h3_ext_bytestream_t *h3_ext_bs, + void *bs_user_data) +{ + user_stream_t *us = bs_user_data; + int ret; + printf("[bytestream]|write callback|\n"); + ret = xqc_server_bytestream_send(h3_ext_bs, us); + if (ret == -XQC_EAGAIN) { + ret = 0; + printf("[bytestream]|write blocked|\n"); + } + return 0; +} + + + #define MAX_HEADER 100 int @@ -687,6 +1212,10 @@ xqc_server_request_create_notify(xqc_h3_request_t *h3_request, void *strm_user_d user_stream->h3_request = h3_request; xqc_h3_request_set_user_data(h3_request, user_stream); + if (g_test_case == 99) { + xqc_h3_request_finish(h3_request); + } + return 0; } @@ -1307,14 +1836,15 @@ xqc_server_close_keylog_file(xqc_server_ctx_t *ctx) void -xqc_keylog_cb(const char *line, void *user_data) +xqc_keylog_cb(const xqc_cid_t *scid, const char *line, void *user_data) { xqc_server_ctx_t *ctx = (xqc_server_ctx_t*)user_data; if (ctx->keylog_fd <= 0) { printf("write keys error!\n"); return; } - + + printf("scid:%s\n", xqc_scid_str(scid)); int write_len = write(ctx->keylog_fd, line, strlen(line)); if (write_len < 0) { printf("write keys failed, errno: %d\n", errno); @@ -1424,6 +1954,11 @@ int main(int argc, char *argv[]) { g_read_body = 0; g_spec_url = 0; g_ipv6 = 0; + g_max_dgram_size = 0; + g_send_dgram = 0; + g_copa_ai = 1.0; + g_copa_delta = 0.05; + g_enable_h3_ext = 1; char server_addr[64] = TEST_ADDR; int server_port = TEST_PORT; @@ -1436,9 +1971,30 @@ int main(int argc, char *argv[]) { //ensure the random sequence is the same for every test srand(0); + int long_opt_index; + + const struct option long_opts[] = { + {"copa_delta", required_argument, &long_opt_index, 1}, + {"copa_ai_unit", required_argument, &long_opt_index, 2}, + {0, 0, 0, 0} + }; + int ch = 0; - while ((ch = getopt(argc, argv, "a:p:ec:Cs:w:r:l:u:x:6bS:MR:o:EK:mLQ")) != -1) { + while ((ch = getopt_long(argc, argv, "a:p:ec:Cs:w:r:l:u:x:6bS:MR:o:EK:mLQ:U:yH", long_opts, NULL)) != -1) { switch (ch) { + case 'H': + printf("option disable h3_ext\n"); + g_enable_h3_ext = 0; + break; + case 'U': + printf("option send_datagram 0 (off), 1 (on), 2(on + batch): %s\n", optarg); + g_send_dgram = atoi(optarg); + break; + case 'Q': + /* max_datagram_frame_size */ + printf("option max_datagram_frame_size: %s\n", optarg); + g_max_dgram_size = atoi(optarg); + break; case 'a': printf("option addr :%s\n", optarg); snprintf(server_addr, sizeof(server_addr), optarg); @@ -1553,10 +2109,42 @@ int main(int argc, char *argv[]) { /* mpshell 限定 */ g_mpshell = 1; break; - case 'Q': + case 'y': printf("option multipath backup path standby :%s\n", "on"); g_mp_backup_mode = 1; break; + /* long options */ + case 0: + + switch (long_opt_index) + { + case 1: /* copa_delta */ + g_copa_delta = atof(optarg); + if (g_copa_delta <= 0 || g_copa_delta > 0.5) { + printf("option g_copa_delta must be in (0, 0.5]\n"); + exit(0); + } else { + printf("option g_copa_delta: %.4lf\n", g_copa_delta); + } + break; + + case 2: /* copa_ai_unit */ + + g_copa_ai = atof(optarg); + if (g_copa_ai < 1.0) { + printf("option g_copa_ai must be greater than 1.0\n"); + exit(0); + } else { + printf("option g_copa_ai: %.4lf\n", g_copa_ai); + } + break; + + default: + break; + } + + break; + default: printf("other option :%c\n", ch); @@ -1642,8 +2230,14 @@ int main(int argc, char *argv[]) { #endif } #endif - else { - printf("unknown cong_ctrl, option is b, r, c\n"); + else if (c_cong_ctl == 'u') { + cong_ctrl = xqc_unlimited_cc_cb; + + } else if (c_cong_ctl == 'P') { + cong_ctrl = xqc_copa_cb; + + } else { + printf("unknown cong_ctrl, option is b, r, c, u\n"); return -1; } printf("congestion control flags: %x\n", cong_flags); @@ -1655,9 +2249,13 @@ int main(int argc, char *argv[]) { .customize_on = 1, .init_cwnd = 32, .cc_optimization_flags = cong_flags, + .copa_delta_ai_unit = g_copa_ai, + .copa_delta_base = g_copa_delta, }, .enable_multipath = g_enable_multipath, .spurious_loss_detect_on = 0, + .max_datagram_frame_size = g_max_dgram_size, + // .datagram_force_retrans_on = 1, }; if (g_test_case == 6) { @@ -1688,6 +2286,18 @@ int main(int argc, char *argv[]) { conn_settings.idle_time_out = 1; } + if (g_test_case == 201) { + conn_settings.max_pkt_out_size = 1216; + } + + if (g_test_case == 208) { + conn_settings.datagram_redundancy = 1; + } + + if (g_test_case == 209) { + conn_settings.datagram_redundant_probe = 30000; + } + xqc_server_set_conn_settings(&conn_settings); xqc_config_t config; @@ -1695,6 +2305,8 @@ int main(int argc, char *argv[]) { return -1; } + config.enable_h3_ext = g_enable_h3_ext; + switch(c_log_level) { case 'e': config.cfg_log_level = XQC_LOG_ERROR; break; case 'i': config.cfg_log_level = XQC_LOG_INFO; break; @@ -1750,7 +2362,19 @@ int main(int argc, char *argv[]) { .h3_request_read_notify = xqc_server_request_read_notify, .h3_request_create_notify = xqc_server_request_create_notify, .h3_request_close_notify = xqc_server_request_close_notify, - } + }, + .h3_ext_dgram_cbs = { + .dgram_read_notify = xqc_server_h3_ext_datagram_read_callback, + .dgram_write_notify = xqc_server_h3_ext_datagram_write_callback, + .dgram_acked_notify = xqc_server_h3_ext_datagram_acked_callback, + .dgram_lost_notify = xqc_server_h3_ext_datagram_lost_callback, + }, + .h3_ext_bs_cbs = { + .bs_read_notify = xqc_h3_ext_bytestream_read_callback, + .bs_write_notify = xqc_h3_ext_bytestream_write_callback, + .bs_create_notify = xqc_h3_ext_bytestream_create_callback, + .bs_close_notify = xqc_h3_ext_bytestream_close_callback, + }, }; /* register transport callbacks */ @@ -1765,7 +2389,13 @@ int main(int argc, char *argv[]) { .stream_read_notify = xqc_server_stream_read_notify, .stream_create_notify = xqc_server_stream_create_notify, .stream_close_notify = xqc_server_stream_close_notify, - } + }, + .dgram_cbs = { + .datagram_acked_notify = xqc_server_datagram_acked_callback, + .datagram_lost_notify = xqc_server_datagram_lost_callback, + .datagram_read_notify = xqc_server_datagram_read_callback, + .datagram_write_notify = xqc_server_datagram_write_callback, + }, }; diff --git a/tests/unittest/main.c b/tests/unittest/main.c index 931b139f8..f6b1e454d 100644 --- a/tests/unittest/main.c +++ b/tests/unittest/main.c @@ -37,6 +37,8 @@ #include "xqc_cid_test.h" #include "xqc_id_hash_test.h" #include "xqc_retry_test.h" +#include "xqc_datagram_test.h" +#include "xqc_h3_ext_test.h" static int xqc_init_suite(void) { return 0; } @@ -97,6 +99,8 @@ main() || !CU_add_test(pSuite, "xqc_cid_test", xqc_test_cid) || !CU_add_test(pSuite, "xqc_test_id_hash", xqc_test_id_hash) || !CU_add_test(pSuite, "xqc_test_retry", xqc_test_retry) + || !CU_add_test(pSuite, "xqc_test_receive_invalid_dgram", xqc_test_receive_invalid_dgram) + || !CU_add_test(pSuite, "xqc_test_h3_ext_frame", xqc_test_h3_ext_frame) /* ADD TESTS HERE */) { CU_cleanup_registry(); diff --git a/tests/unittest/xqc_datagram_test.c b/tests/unittest/xqc_datagram_test.c new file mode 100644 index 000000000..8a3b3a77d --- /dev/null +++ b/tests/unittest/xqc_datagram_test.c @@ -0,0 +1,49 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#include "xqc_datagram_test.h" +#include +#include "src/transport/xqc_conn.h" +#include "src/transport/xqc_engine.h" +#include "src/transport/xqc_frame.h" +#include "src/transport/xqc_frame_parser.h" +#include "src/transport/xqc_packet_out.h" +#include "src/transport/xqc_packet_in.h" +#include "xqc_common_test.h" + +void +xqc_test_receive_invalid_dgram() +{ + xqc_int_t ret; + + xqc_connection_t *conn = test_engine_connect(); + CU_ASSERT(conn != NULL); + + const unsigned char payload[100]; + + xqc_packet_out_t *packet_out; + packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); + CU_ASSERT(packet_out != NULL); + + ret = xqc_gen_datagram_frame(packet_out, payload, (size_t)100); + CU_ASSERT(ret == XQC_OK); + + xqc_packet_in_t pkt_in; + pkt_in.pos = packet_out->po_payload; + pkt_in.last = packet_out->po_buf + packet_out->po_used_size; + conn->local_settings.max_datagram_frame_size = 0; + + ret = xqc_process_datagram_frame(conn, &pkt_in); + CU_ASSERT(ret == -XQC_EPROTO); + + conn->local_settings.max_datagram_frame_size = 50; + ret = xqc_process_datagram_frame(conn, &pkt_in); + CU_ASSERT(ret == -XQC_EPROTO); + + conn->local_settings.max_datagram_frame_size = 120; + ret = xqc_process_datagram_frame(conn, &pkt_in); + CU_ASSERT(ret == XQC_OK); + + xqc_engine_destroy(conn->engine); +} \ No newline at end of file diff --git a/tests/unittest/xqc_datagram_test.h b/tests/unittest/xqc_datagram_test.h new file mode 100644 index 000000000..a162d5042 --- /dev/null +++ b/tests/unittest/xqc_datagram_test.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#ifndef _XQC_DATAGRAM_TEST_H_INCLUDED_ +#define _XQC_DATAGRAM_TEST_H_INCLUDED_ + +void xqc_test_receive_invalid_dgram(); + +#endif /* _XQC_CUBIC_TEST_H_INCLUDED_ */ diff --git a/tests/unittest/xqc_h3_ext_test.c b/tests/unittest/xqc_h3_ext_test.c new file mode 100644 index 000000000..0da581d0e --- /dev/null +++ b/tests/unittest/xqc_h3_ext_test.c @@ -0,0 +1,87 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#include +#include +#include +#include "src/http3/frame/xqc_h3_frame.h" +#include "src/http3/xqc_h3_conn.h" +#include "src/http3/xqc_h3_stream.h" +#include "src/http3/xqc_h3_ext_bytestream.h" + +#include "xqc_common_test.h" + + +ssize_t +xqc_test_h3_ext_frame_parse(const char *p, size_t sz, xqc_h3_frame_pctx_t *state) +{ + ssize_t offset = 0; + while (offset < sz) { + ssize_t len = rand() % sz + 1; + ssize_t ret = xqc_h3_frm_parse(p + offset, len, state); + if (ret < 0) { + return ret; + } + if (ret == 0) { + return offset; + } + offset += ret; + if (state->state == XQC_H3_FRM_STATE_END) { + return offset; + } + } + return XQC_ERROR; +} + +void +xqc_test_h3_ext_frame() +{ + uint64_t push_id = 10; + xqc_h3_ext_frame_bidi_stream_type_t stream_type; + + xqc_list_head_t send_buf; + xqc_init_list_head(&send_buf); + + xqc_h3_frame_pctx_t pctx; + memset(&pctx, 0, sizeof(xqc_h3_frame_pctx_t)); + ssize_t processed; + + /* write */ + /* write bidi_stream_type frame */ + xqc_int_t ret = xqc_h3_ext_frm_write_bidi_stream_type(&send_buf, XQC_H3_BIDI_STREAM_TYPE_REQUEST, XQC_TRUE); + CU_ASSERT(ret == XQC_OK); + /* write bidi_stream_type frame */ + ret = xqc_h3_ext_frm_write_bidi_stream_type(&send_buf, XQC_H3_BIDI_STREAM_TYPE_BYTESTREAM, XQC_TRUE); + CU_ASSERT(ret == XQC_OK); + + xqc_var_buf_t *buf = xqc_var_buf_create(XQC_VAR_BUF_INIT_SIZE); + xqc_list_head_t *pos, *next; + xqc_list_for_each_safe(pos, next, &send_buf) { + xqc_list_buf_t *list_buf = xqc_list_entry(pos, xqc_list_buf_t, list_head); + xqc_var_buf_t *data_buf = list_buf->buf; + xqc_var_buf_save_data(buf, data_buf->data, data_buf->data_len); + + xqc_list_del(&list_buf->list_head); + xqc_var_buf_free(data_buf); + xqc_free(list_buf); + } + + /* parse */ + /* parse bidi_stream_type frame */ + processed = xqc_test_h3_ext_frame_parse(buf->data + buf->consumed_len, buf->data_len - buf->consumed_len, &pctx); + CU_ASSERT(processed > 0); + CU_ASSERT(pctx.state == XQC_H3_FRM_STATE_END); + CU_ASSERT(pctx.frame.frame_payload.stream_type.stream_type.vi == XQC_H3_BIDI_STREAM_TYPE_REQUEST); + buf->consumed_len += processed; + xqc_h3_frm_reset_pctx(&pctx); + /* parse bidi_stream_type frame */ + processed = xqc_test_h3_ext_frame_parse(buf->data + buf->consumed_len, buf->data_len - buf->consumed_len, &pctx); + CU_ASSERT(processed > 0); + CU_ASSERT(pctx.state == XQC_H3_FRM_STATE_END); + CU_ASSERT(pctx.frame.frame_payload.stream_type.stream_type.vi == XQC_H3_BIDI_STREAM_TYPE_BYTESTREAM); + buf->consumed_len += processed; + xqc_h3_frm_reset_pctx(&pctx); + + xqc_var_buf_free(buf); +} \ No newline at end of file diff --git a/tests/unittest/xqc_h3_ext_test.h b/tests/unittest/xqc_h3_ext_test.h new file mode 100644 index 000000000..ae4312484 --- /dev/null +++ b/tests/unittest/xqc_h3_ext_test.h @@ -0,0 +1,10 @@ +/** + * @copyright Copyright (c) 2022, Alibaba Group Holding Limited + */ + +#ifndef XQUIC_XQC_H3_EXT_TEST_H +#define XQUIC_XQC_H3_EXT_TEST_H + +void xqc_test_h3_ext_frame(); + +#endif //XQUIC_XQC_H3_EXT_TEST_H diff --git a/tests/unittest/xqc_process_frame_test.c b/tests/unittest/xqc_process_frame_test.c index 265d05549..5f33bb270 100644 --- a/tests/unittest/xqc_process_frame_test.c +++ b/tests/unittest/xqc_process_frame_test.c @@ -12,7 +12,6 @@ char XQC_TEST_ILL_FRAME_1[] = {0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; char XQC_TEST_ZERO_LEN_NEW_TOKEN_FRAME[] = {0x07, 0x00}; -char XQC_TEST_OVERAGE_STREAM_BLOCKED_FRAME[] = {0x16, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; void @@ -32,11 +31,6 @@ xqc_test_process_frame() ret = xqc_process_frames(conn, &packet_in); CU_ASSERT(ret == -XQC_EPROTO); - packet_in.pos = XQC_TEST_OVERAGE_STREAM_BLOCKED_FRAME; - packet_in.last = packet_in.pos + sizeof(XQC_TEST_OVERAGE_STREAM_BLOCKED_FRAME); - ret = xqc_process_frames(conn, &packet_in); - CU_ASSERT(ret == -XQC_EPARAM); - xqc_engine_destroy(conn->engine); }