Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorTobias Nießen <tniessen@tnie.de>2022-09-18 15:06:09 +0300
committerRafaelGSS <rafael.nunu@hotmail.com>2022-09-27 01:07:45 +0300
commitca5fb67b4ecc145578fc84e964253b10ff41070a (patch)
treeb2817678dcb023532dd133367664b48f111f8338 /deps
parent4436ffb536a46a0390e2c3b91ed50ee1f1da0ef0 (diff)
deps: update to ngtcp2 0.8.1 and nghttp3 0.7.0
Refs: https://github.com/nodejs/node/pull/44619 Co-authored-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/44622 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Diffstat (limited to 'deps')
-rw-r--r--deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h804
-rw-r--r--deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h4
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c91
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h92
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conn.c1853
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conn.h142
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conv.c1
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conv.h4
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_err.c12
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_frame.c66
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_frame.h94
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c93
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h21
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_http.c986
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_http.h100
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c12
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h13
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c184
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h67
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_macro.h4
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_map.c361
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_map.h63
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_mem.c81
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_mem.h39
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c41
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h141
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_opl.c47
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_opl.h66
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_pq.h2
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c282
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h85
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c5
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h7
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_stream.c284
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_stream.h195
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c2
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h3
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_vec.c21
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_vec.h6
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_version.c2
-rw-r--r--deps/ngtcp2/ngtcp2.gyp13
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c293
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h453
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h42
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h42
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h246
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h106
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c325
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c697
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/shared.c755
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/shared.h141
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c524
-rw-r--r--deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h2770
-rw-r--r--deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h4
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c79
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h60
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c58
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h17
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c90
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h91
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c692
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h156
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c1486
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h149
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c24
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h33
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c90
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h300
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c60
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h72
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c4980
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h407
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c9
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h107
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c218
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h48
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c18
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c92
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h21
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c11
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h8
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c184
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h66
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c125
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h51
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c358
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h64
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c70
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h31
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h136
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c40
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h140
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c46
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h65
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c29
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c247
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h57
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c160
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h123
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c4
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c30
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h21
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c259
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h19
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h6
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c20
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h22
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c24
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c36
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h13
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c735
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h214
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c14
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h19
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c137
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h190
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c31
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h20
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c2
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c99
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h65
121 files changed, 19135 insertions, 7000 deletions
diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h
index bc66c40ce21..cd10e4def70 100644
--- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h
+++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h
@@ -128,13 +128,6 @@ typedef ptrdiff_t nghttp3_ssize;
/**
* @macro
*
- * :macro:`NGHTTP3_ERR_PUSH_ID_BLOCKED` indicates that there are no
- * spare push ID available.
- */
-#define NGHTTP3_ERR_PUSH_ID_BLOCKED -106
-/**
- * @macro
- *
* :macro:`NGHTTP3_ERR_MALFORMED_HTTP_HEADER` indicates that an HTTP
* header field is malformed.
*/
@@ -170,13 +163,6 @@ typedef ptrdiff_t nghttp3_ssize;
/**
* @macro
*
- * :macro:`NGHTTP3_ERR_IGNORE_STREAM` indicates that a stream should
- * be ignored.
- */
-#define NGHTTP3_ERR_IGNORE_STREAM -113
-/**
- * @macro
- *
* :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` indicates that a stream is
* not found.
*/
@@ -184,17 +170,17 @@ typedef ptrdiff_t nghttp3_ssize;
/**
* @macro
*
- * :macro:`NGHTTP3_ERR_IGNORE_PUSH_PROMISE` indicates that a push
- * promise should be ignored.
+ * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is
+ * closing state.
*/
-#define NGHTTP3_ERR_IGNORE_PUSH_PROMISE -115
+#define NGHTTP3_ERR_CONN_CLOSING -116
/**
* @macro
*
- * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is
- * closing state.
+ * :macro:`NGHTTP3_ERR_STREAM_DATA_OVERFLOW` indicates that the length
+ * of stream data is too long and causes overflow.
*/
-#define NGHTTP3_ERR_CONN_CLOSING -116
+#define NGHTTP3_ERR_STREAM_DATA_OVERFLOW -117
/**
* @macro
*
@@ -451,59 +437,69 @@ typedef ptrdiff_t nghttp3_ssize;
/**
* @functypedef
*
- * Custom memory allocator to replace malloc(). The |mem_user_data|
- * is the mem_user_data member of :type:`nghttp3_mem` structure.
+ * :type:`nghttp3_malloc` is a custom memory allocator to replace
+ * :manpage:`malloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
*/
-typedef void *(*nghttp3_malloc)(size_t size, void *mem_user_data);
+typedef void *(*nghttp3_malloc)(size_t size, void *user_data);
/**
* @functypedef
*
- * Custom memory allocator to replace free(). The |mem_user_data| is
- * the mem_user_data member of :type:`nghttp3_mem` structure.
+ * :type:`nghttp3_free` is a custom memory allocator to replace
+ * :manpage:`free(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
*/
-typedef void (*nghttp3_free)(void *ptr, void *mem_user_data);
+typedef void (*nghttp3_free)(void *ptr, void *user_data);
/**
* @functypedef
*
- * Custom memory allocator to replace calloc(). The |mem_user_data|
- * is the mem_user_data member of :type:`nghttp3_mem` structure.
+ * :type:`nghttp3_calloc` is a custom memory allocator to replace
+ * :manpage:`calloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
*/
-typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *mem_user_data);
+typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *user_data);
/**
* @functypedef
*
- * Custom memory allocator to replace realloc(). The |mem_user_data|
- * is the mem_user_data member of :type:`nghttp3_mem` structure.
+ * :type:`nghttp3_realloc` is a custom memory allocator to replace
+ * :manpage:`realloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
*/
-typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data);
+typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *user_data);
/**
* @struct
*
* :type:`nghttp3_mem` is a custom memory allocator functions and user
- * defined pointer. The |mem_user_data| member is passed to each
+ * defined pointer. The :member:`user_data` field is passed to each
* allocator function. This can be used, for example, to achieve
* per-session memory pool.
*
* In the following example code, ``my_malloc``, ``my_free``,
* ``my_calloc`` and ``my_realloc`` are the replacement of the
- * standard allocators ``malloc``, ``free``, ``calloc`` and
- * ``realloc`` respectively::
+ * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`,
+ * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively::
*
- * void *my_malloc_cb(size_t size, void *mem_user_data) {
+ * void *my_malloc_cb(size_t size, void *user_data) {
+ * (void)user_data;
* return my_malloc(size);
* }
*
- * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }
+ * void my_free_cb(void *ptr, void *user_data) {
+ * (void)user_data;
+ * my_free(ptr);
+ * }
*
- * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {
+ * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) {
+ * (void)user_data;
* return my_calloc(nmemb, size);
* }
*
- * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {
+ * void *my_realloc_cb(void *ptr, size_t size, void *user_data) {
+ * (void)user_data;
* return my_realloc(ptr, size);
* }
*
@@ -516,27 +512,28 @@ typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data);
*/
typedef struct nghttp3_mem {
/**
- * :member:`mem_user_data` is an arbitrary user supplied data. This
+ * :member:`user_data` is an arbitrary user supplied data. This
* is passed to each allocator function.
*/
- void *mem_user_data;
+ void *user_data;
/**
* :member:`malloc` is a custom allocator function to replace
- * malloc().
+ * :manpage:`malloc(3)`.
*/
nghttp3_malloc malloc;
/**
- * :member:`free` is a custom allocator function to replace free().
+ * :member:`free` is a custom allocator function to replace
+ * :manpage:`free(3)`.
*/
nghttp3_free free;
/**
* :member:`calloc` is a custom allocator function to replace
- * calloc().
+ * :manpage:`calloc(3)`.
*/
nghttp3_calloc calloc;
/**
* :member:`realloc` is a custom allocator function to replace
- * realloc().
+ * :manpage:`realloc(3)`.
*/
nghttp3_realloc realloc;
} nghttp3_mem;
@@ -652,8 +649,8 @@ NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf);
* @function
*
* `nghttp3_buf_free` frees resources allocated for |buf| using |mem|
- * as memory allocator. buf->begin must be a heap buffer allocated by
- * |mem|.
+ * as memory allocator. :member:`buf->begin <nghttp3_buf.begin>` must
+ * be a heap buffer allocated by |mem|.
*/
NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem);
@@ -662,7 +659,8 @@ NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem);
*
* `nghttp3_buf_left` returns the number of additional bytes which can
* be written to the underlying buffer. In other words, it returns
- * buf->end - buf->last.
+ * :member:`buf->end <nghttp3_buf.end>` - :member:`buf->last
+ * <nghttp3_buf.last>`.
*/
NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf);
@@ -670,14 +668,17 @@ NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf);
* @function
*
* `nghttp3_buf_len` returns the number of bytes left to read. In
- * other words, it returns buf->last - buf->pos.
+ * other words, it returns :member:`buf->last <nghttp3_buf.last>` -
+ * :member:`buf->pos <nghttp3_buf.pos>`.
*/
NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf);
/**
* @function
*
- * `nghttp3_buf_reset` sets buf->pos and buf->last to buf->begin.
+ * `nghttp3_buf_reset` sets :member:`buf->pos <nghttp3_buf.pos>` and
+ * :member:`buf->last <nghttp3_buf.last>` to :member:`buf->begin
+ * <nghttp3_buf.begin>`.
*/
NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
@@ -692,7 +693,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
*
* :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set.
*/
-#define NGHTTP3_NV_FLAG_NONE 0
+#define NGHTTP3_NV_FLAG_NONE 0x00u
/**
* @macro
@@ -701,7 +702,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
* pair must not be indexed. Other implementation calls this bit as
* "sensitive".
*/
-#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01
+#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01u
/**
* @macro
@@ -710,7 +711,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
* If this flag is set, the library does not make a copy of header
* field name. This could improve performance.
*/
-#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02
+#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02u
/**
* @macro
@@ -719,7 +720,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
* application. If this flag is set, the library does not make a copy
* of header field value. This could improve performance.
*/
-#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04
+#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04u
/**
* @struct
@@ -748,7 +749,7 @@ typedef struct nghttp3_nv {
size_t valuelen;
/**
* :member:`flags` is bitwise OR of one or more of
- * NGHTTP3_NV_FLAG_*.
+ * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
*/
uint8_t flags;
} nghttp3_nv;
@@ -1057,7 +1058,7 @@ typedef enum nghttp3_qpack_token {
* :type:`nghttp3_nv` and has reference counted buffers and tokens
* which might be useful for applications.
*/
-typedef struct {
+typedef struct nghttp3_qpack_nv {
/**
* :member:`name` is the buffer containing header field name.
* NULL-termination is guaranteed.
@@ -1076,7 +1077,7 @@ typedef struct {
int32_t token;
/**
* :member:`flags` is a bitwise OR of one or more of
- * NGHTTP3_NV_FLAG_*. See :macro:`NGHTTP3_NV_FLAG_NONE`.
+ * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
*/
uint8_t flags;
} nghttp3_qpack_nv;
@@ -1092,11 +1093,14 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder;
* @function
*
* `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder|
- * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic
- * table size. |max_blocked| is the maximum number of streams which
- * can be blocked. |mem| is a memory allocator. This function
- * allocates memory for :type:`nghttp3_qpack_encoder` itself and
- * assigns its pointer to |*pencoder| if it succeeds.
+ * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper
+ * bound of the dynamic table capacity. |mem| is a memory allocator.
+ * This function allocates memory for :type:`nghttp3_qpack_encoder`
+ * itself and assigns its pointer to |*pencoder| if it succeeds.
+ *
+ * The maximum dynamic table capacity is still 0. In order to change
+ * the maximum dynamic table capacity, call
+ * `nghttp3_qpack_encoder_set_max_dtable_capacity`.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -1105,8 +1109,7 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder;
* Out of memory.
*/
NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
- size_t max_dtable_size,
- size_t max_blocked,
+ size_t hard_max_dtable_capacity,
const nghttp3_mem *mem);
/**
@@ -1174,82 +1177,24 @@ NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder(
/**
* @function
*
- * `nghttp3_qpack_encoder_set_max_dtable_size` sets max dynamic table
- * size to |max_dtable_size|.
- *
- * This function returns the number of bytes read, or one of the
- * following negative error codes:
- *
- * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
- * |max_dtable_size| exceeds the hard limit that decoder specifies.
- */
-NGHTTP3_EXTERN int
-nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder,
- size_t max_dtable_size);
-
-/**
- * @function
- *
- * `nghttp3_qpack_encoder_set_hard_max_dtable_size` sets hard maximum
- * dynamic table size to |hard_max_dtable_size|.
- *
- * This function returns the number of bytes read, or one of the
- * following negative error codes:
- *
- * TBD
- */
-NGHTTP3_EXTERN int
-nghttp3_qpack_encoder_set_hard_max_dtable_size(nghttp3_qpack_encoder *encoder,
- size_t hard_max_dtable_size);
-
-/**
- * @function
- *
- * `nghttp3_qpack_encoder_set_max_blocked` sets the number of streams
- * which can be blocked to |max_blocked|.
- *
- * This function returns the number of bytes read, or one of the
- * following negative error codes:
- *
- * TBD
- */
-NGHTTP3_EXTERN int
-nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder,
- size_t max_blocked);
-
-/**
- * @function
- *
- * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header
- * block for a stream denoted by |stream_id| was acknowledged by
- * decoder. This function is provided for debugging purpose only. In
- * HTTP/3, |encoder| knows acknowledgement of header block by reading
- * decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
+ * `nghttp3_qpack_encoder_set_max_dtable_capacity` sets max dynamic
+ * table capacity to |max_dtable_capacity|. If |max_dtable_capacity| is
+ * larger than ``hard_max_dtable_capacity`` parameter of
+ * `nghttp3_qpack_encoder_new`, it is truncated to the latter.
*/
NGHTTP3_EXTERN void
-nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
- int64_t stream_id);
+nghttp3_qpack_encoder_set_max_dtable_capacity(nghttp3_qpack_encoder *encoder,
+ size_t max_dtable_capacity);
/**
* @function
*
- * `nghttp3_qpack_encoder_add_insert_count` increments known received
- * count of |encoder| by |n|. This function is provided for debugging
- * purpose only. In HTTP/3, |encoder| increments known received count
- * by reading decoder stream with
- * `nghttp3_qpack_encoder_read_decoder()`.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * :macro:`NGHTTP3_ERR_NOMEM`
- * Out of memory.
- * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
- * |n| is too large.
+ * `nghttp3_qpack_encoder_set_max_blocked_streams` sets the number of
+ * streams which can be blocked to |max_blocked_streams|.
*/
-NGHTTP3_EXTERN int
-nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder,
- uint64_t n);
+NGHTTP3_EXTERN void
+nghttp3_qpack_encoder_set_max_blocked_streams(nghttp3_qpack_encoder *encoder,
+ size_t max_blocked_streams);
/**
* @function
@@ -1265,23 +1210,11 @@ nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder);
/**
* @function
*
- * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream
- * denoted by |stream_id| is cancelled. This function is provided for
- * debugging purpose only. In HTTP/3, |encoder| knows this by reading
- * decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
- */
-NGHTTP3_EXTERN void
-nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
- int64_t stream_id);
-
-/**
- * @function
- *
- * `nghttp3_qpack_encoder_get_num_blocked` returns the number of
- * streams which are potentially blocked at decoder side.
+ * `nghttp3_qpack_encoder_get_num_blocked_streams` returns the number
+ * of streams which are potentially blocked at decoder side.
*/
NGHTTP3_EXTERN size_t
-nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder);
+nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder);
/**
* @struct
@@ -1351,11 +1284,12 @@ typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder;
* @function
*
* `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder|
- * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic
- * table size. |max_blocked| is the maximum number of streams which
- * can be blocked. |mem| is a memory allocator. This function
- * allocates memory for :type:`nghttp3_qpack_decoder` itself and
- * assigns its pointer to |*pdecoder| if it succeeds.
+ * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper
+ * bound of the dynamic table capacity. |max_blocked_streams| is the
+ * maximum number of streams which can be blocked. |mem| is a memory
+ * allocator. This function allocates memory for
+ * :type:`nghttp3_qpack_decoder` itself and assigns its pointer to
+ * |*pdecoder| if it succeeds.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -1364,8 +1298,8 @@ typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder;
* Out of memory.
*/
NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
- size_t max_dtable_size,
- size_t max_blocked,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
const nghttp3_mem *mem);
/**
@@ -1415,7 +1349,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
*
* :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set.
*/
-#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0
+#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0x00u
/**
* @macro
@@ -1423,7 +1357,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
* :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header
* field is successfully decoded.
*/
-#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01
+#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01u
/**
* @macro
@@ -1431,7 +1365,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
* :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header
* fields have been decoded.
*/
-#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02
+#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02u
/**
* @macro
@@ -1439,7 +1373,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
* :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding
* has been blocked.
*/
-#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04
+#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04u
/**
* @function
@@ -1459,12 +1393,19 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
* due to required insert count.
*
* When a header field is decoded, an application receives it in |nv|.
- * nv->name and nv->value are reference counted buffer, and their
+ * :member:`nv->name <nghttp3_qpack_nv.name>` and :member:`nv->value
+ * <nghttp3_qpack_nv.value>` are reference counted buffer, and their
* reference counts are already incremented for application use.
* Therefore, when application finishes processing the header field,
* it must call `nghttp3_rcbuf_decref(nv->name)
* <nghttp3_rcbuf_decref>` and `nghttp3_rcbuf_decref(nv->value)
- * <nghttp3_rcbuf_decref>` or memory leak might occur.
+ * <nghttp3_rcbuf_decref>` or memory leak might occur. These
+ * :type:`nghttp3_rcbuf` objects hold the pointer to
+ * :type:`nghttp3_mem` that is passed to `nghttp3_qpack_decoder_new`
+ * (or either `nghttp3_conn_client_new` or `nghttp3_conn_server_new`
+ * if it is used indirectly). As long as these objects are alive, the
+ * pointed :type:`nghttp3_mem` object must be available. Otherwise,
+ * `nghttp3_rcbuf_decref` will cause undefined behavior.
*
* This function returns the number of bytes read, or one of the
* following negative error codes:
@@ -1529,14 +1470,24 @@ nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
/**
* @function
*
- * `nghttp3_qpack_decoder_set_dtable_cap` sets |cap| as maximum
- * dynamic table size. Normally, the maximum capacity is communicated
- * in encoder stream. This function is provided for debugging and
- * testing purpose.
+ * `nghttp3_qpack_decoder_set_max_dtable_capacity` sets
+ * |max_dtable_capacity| as maximum dynamic table size.
+ * |max_dtable_capacity| must be equal to or smaller than
+ * ``hard_max_dtable_capacity`` parameter of
+ * `nghttp3_qpack_decoder_new`. Normally, the maximum capacity is
+ * communicated in encoder stream. This function is provided for
+ * debugging and testing purpose.
+ *
+ * This function returns 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * |max_dtable_capacity| exceeds the upper bound of the dynamic
+ * table capacity.
*/
-NGHTTP3_EXTERN void
-nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder,
- size_t cap);
+NGHTTP3_EXTERN int
+nghttp3_qpack_decoder_set_max_dtable_capacity(nghttp3_qpack_decoder *decoder,
+ size_t max_dtable_capacity);
/**
* @function
@@ -1571,10 +1522,10 @@ NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr);
*
* :type:`nghttp3_debug_vprintf_callback` is a callback function
* invoked when the library outputs debug logging. The function is
- * called with arguments suitable for ``vfprintf(3)``
+ * called with arguments suitable for :manpage:`vfprintf(3)`.
*
* The debug output is only enabled if the library is built with
- * ``DEBUGBUILD`` macro defined.
+ * :macro:`DEBUGBUILD` macro defined.
*/
typedef void (*nghttp3_debug_vprintf_callback)(const char *format,
va_list args);
@@ -1583,28 +1534,52 @@ typedef void (*nghttp3_debug_vprintf_callback)(const char *format,
* @function
*
* `nghttp3_set_debug_vprintf_callback` sets a debug output callback
- * called by the library when built with ``DEBUGBUILD`` macro defined.
- * If this option is not used, debug log is written into standard
- * error output.
+ * called by the library when built with :macro:`DEBUGBUILD` macro
+ * defined. If this option is not used, debug log is written into
+ * standard error output.
*
- * For builds without ``DEBUGBUILD`` macro defined, this function is
- * noop.
+ * For builds without :macro:`DEBUGBUILD` macro defined, this function
+ * is noop.
*
- * Note that building with ``DEBUGBUILD`` may cause significant
+ * Note that building with :macro:`DEBUGBUILD` may cause significant
* performance penalty to libnghttp3 because of extra processing. It
* should be used for debugging purpose only.
*
* .. Warning::
*
- * Building with ``DEBUGBUILD`` may cause significant performance
- * penalty to libnghttp3 because of extra processing. It should be
- * used for debugging purpose only. We write this two times because
- * this is important.
+ * Building with :macro:`DEBUGBUILD` may cause significant
+ * performance penalty to libnghttp3 because of extra processing.
+ * It should be used for debugging purpose only. We write this two
+ * times because this is important.
*/
NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback(
nghttp3_debug_vprintf_callback debug_vprintf_callback);
/**
+ * @macrosection
+ *
+ * Shutdown related constants
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` specifies stream id sent
+ * by a server when it initiates graceful shutdown of the connection
+ * via `nghttp3_conn_submit_shutdown_notice`.
+ */
+#define NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID ((1ull << 62) - 4)
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID` specifies push id sent
+ * by a client when it initiates graceful shutdown of the connection
+ * via `nghttp3_conn_submit_shutdown_notice`.
+ */
+#define NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID ((1ull << 62) - 1)
+
+/**
* @struct
*
* :type:`nghttp3_conn` represents a single HTTP/3 connection.
@@ -1625,7 +1600,7 @@ typedef struct nghttp3_conn nghttp3_conn;
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
*/
typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id,
- size_t datalen, void *conn_user_data,
+ uint64_t datalen, void *conn_user_data,
void *stream_user_data);
/**
@@ -1711,7 +1686,7 @@ typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id,
* |name| contains a field name and |value| contains a field value.
* |token| is one of token defined in :type:`nghttp3_qpack_token` or
* -1 if no token is defined for |name|. |flags| is bitwise OR of
- * zero or more of NGHTTP3_NV_FLAG_*.
+ * zero or more of :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
*
* The buffers for |name| and |value| are reference counted. If
* application needs to keep them, increment the reference count with
@@ -1735,81 +1710,20 @@ typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id,
* :type:`nghttp3_end_headers` is a callback function which is invoked
* when an incoming header block has ended.
*
+ * If the stream ends with this header block, |fin| is set to nonzero.
+ *
* The implementation of this callback must return 0 if it succeeds.
* Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
* caller immediately. Any values other than 0 is treated as
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
*/
typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id,
- void *conn_user_data,
+ int fin, void *conn_user_data,
void *stream_user_data);
/**
* @functypedef
*
- * :type:`nghttp3_begin_push_promise` is a callback function which is
- * invoked when an incoming header block section in PUSH_PROMISE is
- * started on a stream denoted by |stream_id|. |push_id| identifies a
- * push promise. Each header field is passed to application by
- * :type:`nghttp3_recv_push_promise` callback. And then
- * :type:`nghttp3_end_push_promise` is called when a whole header
- * block is processed.
- *
- * The implementation of this callback must return 0 if it succeeds.
- * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
- * caller immediately. Any values other than 0 is treated as
- * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
- */
-typedef int (*nghttp3_begin_push_promise)(nghttp3_conn *conn, int64_t stream_id,
- int64_t push_id, void *conn_user_data,
- void *stream_user_data);
-
-/**
- * @functypedef
- *
- * :type:`nghttp3_recv_push_promise` is a callback function which is
- * invoked when a header field in PUSH_PROMISE is received on a stream
- * denoted by |stream_id|. |push_id| identifies a push promise.
- * |name| contains a field name and |value| contains a field value.
- * |token| is one of token defined in :type:`nghttp3_qpack_token` or
- * -1 if no token is defined for |name|. |flags| is bitwise OR of
- * zero or more of NGHTTP3_NV_FLAG_*.
- *
- * The buffers for |name| and |value| are reference counted. If
- * application needs to keep them, increment the reference count with
- * `nghttp3_rcbuf_incref`. When they are no longer used, call
- * `nghttp3_rcbuf_decref`.
- *
- * The implementation of this callback must return 0 if it succeeds.
- * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
- * caller immediately. Any values other than 0 is treated as
- * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
- */
-typedef int (*nghttp3_recv_push_promise)(nghttp3_conn *conn, int64_t stream_id,
- int64_t push_id, int32_t token,
- nghttp3_rcbuf *name,
- nghttp3_rcbuf *value, uint8_t flags,
- void *conn_user_data,
- void *stream_user_data);
-
-/**
- * @functypedef
- *
- * :type:`nghttp3_end_push_promise` is a callback function which is
- * invoked when an incoming header block in PUSH_PROMISE has ended.
- *
- * The implementation of this callback must return 0 if it succeeds.
- * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
- * caller immediately. Any values other than 0 is treated as
- * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
- */
-typedef int (*nghttp3_end_push_promise)(nghttp3_conn *conn, int64_t stream_id,
- int64_t push_id, void *conn_user_data,
- void *stream_user_data);
-
-/**
- * @functypedef
- *
* :type:`nghttp3_end_stream` is a callback function which is invoked
* when the receiving side of stream is closed. For server, this
* callback function is invoked when HTTP request is received
@@ -1827,26 +1741,7 @@ typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id,
/**
* @functypedef
*
- * :type:`nghttp3_cancel_push` is a callback function which is invoked
- * when the push identified by |push_id| is cancelled by remote
- * endpoint. If a stream has been bound to the push ID, |stream_id|
- * contains the stream ID and |stream_user_data| points to the stream
- * user data. Otherwise, |stream_id| is -1 and |stream_user_data| is
- * NULL.
- *
- * The implementation of this callback must return 0 if it succeeds.
- * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
- * caller immediately. Any values other than 0 is treated as
- * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
- */
-typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id,
- int64_t stream_id, void *conn_user_data,
- void *stream_user_data);
-
-/**
- * @functypedef
- *
- * :type:`nghttp3_send_stop_sending` is a callback function which is
+ * :type:`nghttp3_stop_sending` is a callback function which is
* invoked when the library asks application to send STOP_SENDING to
* the stream identified by |stream_id|. |app_error_code| indicates
* the reason for this action.
@@ -1856,43 +1751,61 @@ typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id,
* caller immediately. Any values other than 0 is treated as
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
*/
-typedef int (*nghttp3_send_stop_sending)(nghttp3_conn *conn, int64_t stream_id,
- uint64_t app_error_code,
- void *conn_user_data,
- void *stream_user_data);
+typedef int (*nghttp3_stop_sending)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
/**
* @functypedef
*
- * :type:`nghttp3_push_stream` is a callback function which is invoked
- * when a push stream identified by |stream_id| is opened with
- * |push_id|.
+ * :type:`nghttp3_reset_stream` is a callback function which is
+ * invoked when the library asks application to reset stream
+ * identified by |stream_id|. |app_error_code| indicates the reason
+ * for this action.
*
* The implementation of this callback must return 0 if it succeeds.
* Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
* caller immediately. Any values other than 0 is treated as
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
*/
-typedef int (*nghttp3_push_stream)(nghttp3_conn *conn, int64_t push_id,
- int64_t stream_id, void *conn_user_data);
+typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
/**
* @functypedef
*
- * :type:`nghttp3_reset_stream` is a callback function which is
- * invoked when the library asks application to reset stream
- * identified by |stream_id|. |app_error_code| indicates the reason
- * for this action.
+ * :type:`nghttp3_shutdown` is a callback function which is invoked
+ * when a shutdown is initiated by the remote endpoint. For client,
+ * |id| contains a stream id of a client initiated stream, for server,
+ * it contains a push id. All client streams with stream id or pushes
+ * with push id equal to or larger than |id| are guaranteed to not be
+ * processed by the remote endpoint.
+ *
+ * Parameter |id| for client can contain a special value
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` and for server it can
+ * contain special value
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID`. These values signal
+ * request for graceful shutdown of the connection, triggered by
+ * remote endpoint's invocation of
+ * `nghttp3_conn_submit_shutdown_notice`.
+ *
+ * It is possible that this callback is invoked multiple times on a
+ * single connection, however the |id| can only stay the same or
+ * decrease, never increase.
*
* The implementation of this callback must return 0 if it succeeds.
* Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
* caller immediately. Any values other than 0 is treated as
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
*/
-typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id,
- uint64_t app_error_code,
- void *conn_user_data,
- void *stream_user_data);
+typedef int (*nghttp3_shutdown)(nghttp3_conn *conn, int64_t id,
+ void *conn_user_data);
+
+#define NGHTTP3_CALLBACKS_VERSION_V1 1
+#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_VERSION_V1
/**
* @struct
@@ -1953,37 +1866,11 @@ typedef struct nghttp3_callbacks {
*/
nghttp3_end_headers end_trailers;
/**
- * :member:`begin_push_promise` is a callback function which is
- * invoked when a push promise has started on a particular stream.
- */
- nghttp3_begin_push_promise begin_push_promise;
- /**
- * :member:`recv_push_promise` is a callback function which is
- * invoked when a single header field in a push promise is received
- * on a particular stream.
- */
- nghttp3_recv_push_promise recv_push_promise;
- /**
- * :member:`end_push_promise` is a callback function which is
- * invoked when a push promise has ended on a particular stream.
- */
- nghttp3_end_push_promise end_push_promise;
- /**
- * :member:`cancel_push` is a callback function which is invoked
- * when a push promise has been cancelled by a remote endpoint.
- */
- nghttp3_cancel_push cancel_push;
- /**
- * :member:`send_stop_sending` is a callback function which is
- * invoked when the library asks application to send STOP_SENDING to
- * a particular stream.
- */
- nghttp3_send_stop_sending send_stop_sending;
- /**
- * :member:`push_stream` is a callback function which is invoked
- * when a push stream has opened.
+ * :member:`stop_sending` is a callback function which is invoked
+ * when the library asks application to send STOP_SENDING to a
+ * particular stream.
*/
- nghttp3_push_stream push_stream;
+ nghttp3_stop_sending stop_sending;
/**
* :member:`end_stream` is a callback function which is invoked when
* a receiving side of stream has been closed.
@@ -1995,34 +1882,53 @@ typedef struct nghttp3_callbacks {
* RESET_STREAM).
*/
nghttp3_reset_stream reset_stream;
+ /**
+ * :member:`shutdown` is a callback function which is invoked when
+ * the remote endpoint has signalled initiation of connection shutdown.
+ */
+ nghttp3_shutdown shutdown;
} nghttp3_callbacks;
+#define NGHTTP3_SETTINGS_VERSION_V1 1
+#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_VERSION_V1
+
/**
* @struct
*
* :type:`nghttp3_settings` defines HTTP/3 settings.
*/
-typedef struct {
+typedef struct nghttp3_settings {
/**
* :member:`max_field_section_size` specifies the maximum header
* section (block) size.
*/
uint64_t max_field_section_size;
/**
- * :member:`max_pushes` specifies the maximum number of concurrent
- * pushes it accepts from a remote endpoint.
+ * :member:`qpack_max_dtable_capacity` is the maximum size of QPACK
+ * dynamic table.
*/
- uint64_t max_pushes;
+ size_t qpack_max_dtable_capacity;
/**
- * :member:`qpack_max_table_capacity` is the maximum size of QPACK
- * dynamic table.
+ * :member:`qpack_encoder_max_dtable_capacity` is the upper bound of
+ * QPACK dynamic table capacity that the QPACK encoder is willing to
+ * use. The effective maximum dynamic table capacity is the minimum
+ * of this field and the value of the received
+ * SETTINGS_QPACK_MAX_TABLE_CAPACITY. If this field is set to 0,
+ * the encoder does not use the dynamic table.
*/
- size_t qpack_max_table_capacity;
+ size_t qpack_encoder_max_dtable_capacity;
/**
* :member:`qpack_blocked_streams` is the maximum number of streams
* which can be blocked while they are being decoded.
*/
size_t qpack_blocked_streams;
+ /**
+ * :member:`enable_connect_protocol`, if set to nonzero, enables
+ * Extended CONNECT Method (see
+ * https://www.ietf.org/archive/id/draft-ietf-httpbis-h3-websockets-00.html).
+ * Client ignores this field.
+ */
+ int enable_connect_protocol;
} nghttp3_settings;
/**
@@ -2030,34 +1936,51 @@ typedef struct {
*
* `nghttp3_settings_default` fills |settings| with the default
* values.
+ *
+ * - :member:`max_field_section_size
+ * <nghttp3_settings.max_field_section_size>` = :expr:`((1ull << 62) - 1)`
+ * - :member:`qpack_max_dtable_capacity
+ * <nghttp3_settings.qpack_max_dtable_capacity>` = 0
+ * - :member:`qpack_encoder_max_dtable_capacity
+ * <nghttp3_settings.qpack_encoder_max_dtable_capacity>` = 4096
+ * - :member:`qpack_blocked_streams
+ * <nghttp3_settings.qpack_blocked_streams>` = 0
+ * - :member:`enable_connect_protocol
+ * <nghttp3_settings.enable_connect_protocol>` = 0
*/
-NGHTTP3_EXTERN void nghttp3_settings_default(nghttp3_settings *settings);
+NGHTTP3_EXTERN void
+nghttp3_settings_default_versioned(int settings_version,
+ nghttp3_settings *settings);
/**
* @function
*
* `nghttp3_conn_client_new` creates :type:`nghttp3_conn` and
* initializes it for client use. The pointer to the object is stored
- * in |*pconn|.
+ * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned
+ * by `nghttp3_mem_default` is used.
*/
-NGHTTP3_EXTERN int nghttp3_conn_client_new(nghttp3_conn **pconn,
- const nghttp3_callbacks *callbacks,
- const nghttp3_settings *settings,
- const nghttp3_mem *mem,
- void *conn_user_data);
+NGHTTP3_EXTERN int
+nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *conn_user_data);
/**
* @function
*
* `nghttp3_conn_server_new` creates :type:`nghttp3_conn` and
* initializes it for server use. The pointer to the object is stored
- * in |*pconn|.
+ * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned
+ * by `nghttp3_mem_default` is used.
*/
-NGHTTP3_EXTERN int nghttp3_conn_server_new(nghttp3_conn **pconn,
- const nghttp3_callbacks *callbacks,
- const nghttp3_settings *settings,
- const nghttp3_mem *mem,
- void *conn_user_data);
+NGHTTP3_EXTERN int
+nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *conn_user_data);
/**
* @function
@@ -2177,8 +2100,8 @@ NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn,
* `nghttp3_conn_block_stream` tells the library that stream
* identified by |stream_id| is blocked due to QUIC flow control.
*/
-NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn,
- int64_t stream_id);
+NGHTTP3_EXTERN void nghttp3_conn_block_stream(nghttp3_conn *conn,
+ int64_t stream_id);
/**
* @function
@@ -2193,13 +2116,44 @@ NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn,
/**
* @function
*
+ * `nghttp3_conn_is_stream_writable` returns nonzero if a stream
+ * identified by |stream_id| is writable. It is not writable if:
+ *
+ * - the stream does not exist; or,
+ * - the stream is closed (e.g., `nghttp3_conn_close_stream` is
+ * called); or,
+ * - the stream is QUIC flow control blocked (e.g.,
+ * `nghttp3_conn_block_stream` is called); or,
+ * - the stream is input data blocked (e.g.,
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK` is returned from
+ * :type:`nghttp3_read_data_callback`); or,
+ * - the stream is half-closed local (e.g.,
+ * `nghttp3_conn_shutdown_stream_write` is called).
+ */
+NGHTTP3_EXTERN int nghttp3_conn_is_stream_writable(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
* `nghttp3_conn_shutdown_stream_write` tells the library that any
* further write operation to stream identified by |stream_id| is
* prohibited. This works like `nghttp3_conn_block_stream`, but it
* cannot be unblocked by `nghttp3_conn_unblock_stream`.
*/
-NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn,
- int64_t stream_id);
+NGHTTP3_EXTERN void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_shutdown_stream_read` tells the library that
+ * read-side of stream denoted by |stream_id| is abruptly closed and
+ * any further incoming data and pending stream data should be
+ * discarded.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn,
+ int64_t stream_id);
/**
* @function
@@ -2215,41 +2169,47 @@ NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn,
*
* `nghttp3_conn_close_stream` closes stream identified by
* |stream_id|. |app_error_code| is the reason of the closure.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM`
+ * A critical stream is closed.
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`
+ * User callback failed
*/
NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn,
int64_t stream_id,
uint64_t app_error_code);
/**
- * @function
- *
- * `nghttp3_conn_reset_stream` must be called if stream identified by
- * |stream_id| is reset by a remote endpoint. This is required in
- * order to cancel QPACK stream.
- */
-NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn,
- int64_t stream_id);
-
-/**
* @macrosection
*
* Data flags
*/
/**
+ * @macro
+ *
* :macro:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set.
*/
-#define NGHTTP3_DATA_FLAG_NONE 0x00
+#define NGHTTP3_DATA_FLAG_NONE 0x00u
/**
+ * @macro
+ *
* :macro:`NGHTTP3_DATA_FLAG_EOF` indicates that all request or
* response body has been provided to the library. It also indicates
* that sending side of stream is closed unless
* :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same time.
*/
-#define NGHTTP3_DATA_FLAG_EOF 0x01
+#define NGHTTP3_DATA_FLAG_EOF 0x01u
/**
+ * @macro
+ *
* :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` indicates that sending
* side of stream is not closed even if :macro:`NGHTTP3_DATA_FLAG_EOF`
* is set. Usually this flag is used to send trailer fields with
@@ -2257,7 +2217,7 @@ NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn,
* `nghttp3_conn_submit_trailers()` has been called, regardless of
* this flag, the submitted trailer fields are sent.
*/
-#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02
+#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02u
/**
* @function
@@ -2320,7 +2280,7 @@ typedef nghttp3_ssize (*nghttp3_read_data_callback)(
* :type:`nghttp3_data_reader` specifies the way how to generate
* request or response body.
*/
-typedef struct {
+typedef struct nghttp3_data_reader {
/**
* :member:`read_data` is a callback function to generate body.
*/
@@ -2346,24 +2306,6 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_request(
/**
* @function
*
- * `nghttp3_conn_submit_push_promise` submits push promise on the
- * stream identified by |stream_id|. |stream_id| must be a client
- * initiated bidirectional stream. Only server can submit push
- * promise. On success, a push ID is assigned to |*ppush_id|. |nva|
- * of length |nvlen| specifies HTTP request header fields. In order
- * to submit HTTP response, first call
- * `nghttp3_conn_bind_push_stream()` and then
- * `nghttp3_conn_submit_response()`.
- */
-NGHTTP3_EXTERN int nghttp3_conn_submit_push_promise(nghttp3_conn *conn,
- int64_t *ppush_id,
- int64_t stream_id,
- const nghttp3_nv *nva,
- size_t nvlen);
-
-/**
- * @function
- *
* `nghttp3_conn_submit_info` submits HTTP non-final response header
* fields on the stream identified by |stream_id|. |nva| of length
* |nvlen| specifies HTTP response header fields.
@@ -2404,32 +2346,9 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn,
/**
* @function
*
- * `nghttp3_conn_bind_push_stream` binds the stream identified by
- * |stream_id| to the push identified by |push_id|. |stream_id| must
- * be a server initiated unidirectional stream. |push_id| must be
- * obtained from `nghttp3_conn_submit_push_promise()`. To send
- * response to this push, call `nghttp3_conn_submit_response()`.
- */
-NGHTTP3_EXTERN int nghttp3_conn_bind_push_stream(nghttp3_conn *conn,
- int64_t push_id,
- int64_t stream_id);
-
-/**
- * @function
- *
- * `nghttp3_conn_cancel_push` cancels the push identified by
- * |push_id|.
- */
-NGHTTP3_EXTERN int nghttp3_conn_cancel_push(nghttp3_conn *conn,
- int64_t push_id);
-
-/**
- * @function
- *
* `nghttp3_conn_submit_shutdown_notice` notifies the other endpoint
- * to stop creating new stream (for server) or push (for client).
- * After a couple of RTTs later, call `nghttp3_conn_shutdown` to start
- * graceful shutdown.
+ * to stop creating new stream. After a couple of RTTs later, call
+ * `nghttp3_conn_shutdown` to start graceful shutdown.
*/
NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn);
@@ -2439,8 +2358,8 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn);
* `nghttp3_conn_shutdown` starts graceful shutdown. It should be
* called after `nghttp3_conn_submit_shutdown_notice` and a couple of
* RTT. After calling this function, the local endpoint starts
- * rejecting new incoming streams (for server) or pushes (for client).
- * The existing streams or pushes are processed normally.
+ * rejecting new incoming streams. The existing streams are processed
+ * normally.
*/
NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn);
@@ -2459,18 +2378,23 @@ NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn,
*
* `nghttp3_conn_get_frame_payload_left` returns the number of bytes
* left to read current frame payload for a stream denoted by
- * |stream_id|. If no such stream is found, it returns
- * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`.
+ * |stream_id|. If no such stream is found, it returns 0.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @macrosection
+ *
+ * HTTP stream priority flags
*/
-NGHTTP3_EXTERN int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
- int64_t stream_id);
/**
* @macro
*
* :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level.
*/
-#define NGHTTP3_DEFAULT_URGENCY 1
+#define NGHTTP3_DEFAULT_URGENCY 3
/**
* @macro
@@ -2518,8 +2442,9 @@ typedef struct nghttp3_pri {
* @function
*
* `nghttp3_conn_get_stream_priority` stores stream priority of a
- * stream denoted by |stream_id| into |*dest|. Only server can use
- * this function.
+ * stream denoted by |stream_id| into |*dest|. |stream_id| must
+ * identify client initiated bidirectional stream. Only server can
+ * use this function.
*
* This function must not be called if |conn| is initialized as
* client.
@@ -2537,15 +2462,22 @@ NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn,
/**
* @function
*
- * `nghttp3_conn_set_stream_priority` updates stream priority of a
- * stream denoted by |stream_id| by the value pointed by |pri|.
+ * `nghttp3_conn_set_stream_priority` updates priority of a stream
+ * denoted by |stream_id| with the value pointed by |pri|.
+ * |stream_id| must identify client initiated bidirectional stream.
*
- * This function must not be called if |conn| is initialized as
- * client.
+ * Both client and server can update stream priority with this
+ * function.
+ *
+ * If server updates stream priority with this function, it completely
+ * overrides stream priority set by client and the attempts to update
+ * priority by client are ignored.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * |stream_id| is not a client initiated bidirectional stream ID.
* :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
* Stream not found.
* :macro:`NGHTTP3_ERR_NOMEM`
@@ -2572,14 +2504,14 @@ nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
* `nghttp3_vec_len` returns the sum of length in |vec| of |cnt|
* elements.
*/
-NGHTTP3_EXTERN size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt);
+NGHTTP3_EXTERN uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt);
/**
* @function
*
* `nghttp3_check_header_name` returns nonzero if HTTP header field
* name |name| of length |len| is valid according to
- * http://tools.ietf.org/html/rfc7230#section-3.2
+ * :rfc:`7230#section-3.2`.
*
* Because this is a header field name in HTTP/3, the upper cased
* alphabet is treated as error.
@@ -2591,7 +2523,7 @@ NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len);
*
* `nghttp3_check_header_value` returns nonzero if HTTP header field
* value |value| of length |len| is valid according to
- * http://tools.ietf.org/html/rfc7230#section-3.2
+ * :rfc:`7230#section-3.2`.
*/
NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len);
@@ -2615,6 +2547,12 @@ NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest,
size_t len);
/**
+ * @macrosection
+ *
+ * nghttp3_info flags
+ */
+
+/**
* @macro
*
* :macro:`NGHTTP3_VERSION_AGE` is the age of :type:`nghttp3_info`.
@@ -2627,7 +2565,7 @@ NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest,
* :type:`nghttp3_info` is what `nghttp3_version()` returns. It holds
* information about the particular nghttp3 version.
*/
-typedef struct {
+typedef struct nghttp3_info {
/**
* :member:`age` is the age of this struct. This instance of
* nghttp3 sets it to :macro:`NGHTTP3_VERSION_AGE` but a future
@@ -2657,7 +2595,49 @@ typedef struct {
* met, this function will return a ``NULL``. Pass in 0 to skip the
* version checking.
*/
-NGHTTP3_EXTERN nghttp3_info *nghttp3_version(int least_version);
+NGHTTP3_EXTERN const nghttp3_info *nghttp3_version(int least_version);
+
+/**
+ * @function
+ *
+ * `nghttp3_err_is_fatal` returns nonzero if |liberr| is a fatal
+ * error. |liberr| must be one of nghttp3 library error codes (which
+ * is defined as NGHTTP3_ERR_* macro, such as
+ * :macro:`NGHTTP3_ERR_NOMEM`).
+ */
+NGHTTP3_EXTERN int nghttp3_err_is_fatal(int liberr);
+
+/*
+ * Versioned function wrappers
+ */
+
+/*
+ * `nghttp3_settings_default` is a wrapper around
+ * `nghttp3_settings_default_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_settings_default(SETTINGS) \
+ nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_VERSION, (SETTINGS))
+
+/*
+ * `nghttp3_conn_client_new` is a wrapper around
+ * `nghttp3_conn_client_new_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_conn_client_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \
+ nghttp3_conn_client_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \
+ (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \
+ (SETTINGS), (MEM), (USER_DATA))
+
+/*
+ * `nghttp3_conn_server_new` is a wrapper around
+ * `nghttp3_conn_server_new_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_conn_server_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \
+ nghttp3_conn_server_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \
+ (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \
+ (SETTINGS), (MEM), (USER_DATA))
#ifdef __cplusplus
}
diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h
index 69d29e9f140..bc57eb2cfcf 100644
--- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h
+++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h
@@ -31,7 +31,7 @@
*
* Version number of the nghttp3 library release.
*/
-#define NGHTTP3_VERSION "0.1.0-DEV"
+#define NGHTTP3_VERSION "0.7.0"
/**
* @macro
@@ -41,6 +41,6 @@
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
* becomes 0x010203.
*/
-#define NGHTTP3_VERSION_NUM 0x000100
+#define NGHTTP3_VERSION_NUM 0x000700
#endif /* NGHTTP3_VERSION_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c
new file mode 100644
index 00000000000..e134d0f4dce
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c
@@ -0,0 +1,91 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_balloc.h"
+
+#include <assert.h>
+
+#include "nghttp3_mem.h"
+
+void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
+ const nghttp3_mem *mem) {
+ assert((blklen & 0xfu) == 0);
+
+ balloc->mem = mem;
+ balloc->blklen = blklen;
+ balloc->head = NULL;
+ nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
+}
+
+void nghttp3_balloc_free(nghttp3_balloc *balloc) {
+ if (balloc == NULL) {
+ return;
+ }
+
+ nghttp3_balloc_clear(balloc);
+}
+
+void nghttp3_balloc_clear(nghttp3_balloc *balloc) {
+ nghttp3_memblock_hd *p, *next;
+
+ for (p = balloc->head; p; p = next) {
+ next = p->next;
+ nghttp3_mem_free(balloc->mem, p);
+ }
+
+ balloc->head = NULL;
+ nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
+}
+
+int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n) {
+ uint8_t *p;
+ nghttp3_memblock_hd *hd;
+
+ assert(n <= balloc->blklen);
+
+ if (nghttp3_buf_left(&balloc->buf) < n) {
+ p = nghttp3_mem_malloc(balloc->mem, sizeof(nghttp3_memblock_hd) + 0x10u +
+ balloc->blklen);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ hd = (nghttp3_memblock_hd *)(void *)p;
+ hd->next = balloc->head;
+ balloc->head = hd;
+ nghttp3_buf_wrap_init(
+ &balloc->buf,
+ (uint8_t *)(((uintptr_t)p + sizeof(nghttp3_memblock_hd) + 0xfu) &
+ ~(uintptr_t)0xfu),
+ balloc->blklen);
+ }
+
+ assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
+
+ *pbuf = balloc->buf.last;
+ balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
+
+ return 0;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h
new file mode 100644
index 00000000000..e02f61d16b5
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h
@@ -0,0 +1,92 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_BALLOC_H
+#define NGHTTP3_BALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_buf.h"
+
+typedef struct nghttp3_memblock_hd nghttp3_memblock_hd;
+
+/*
+ * nghttp3_memblock_hd is the header of memory block.
+ */
+struct nghttp3_memblock_hd {
+ nghttp3_memblock_hd *next;
+};
+
+/*
+ * nghttp3_balloc is a custom memory allocator. It allocates |blklen|
+ * bytes of memory at once on demand, and returns its slice when the
+ * allocation is requested.
+ */
+typedef struct nghttp3_balloc {
+ /* mem is the underlying memory allocator. */
+ const nghttp3_mem *mem;
+ /* blklen is the size of memory block. */
+ size_t blklen;
+ /* head points to the list of memory block allocated so far. */
+ nghttp3_memblock_hd *head;
+ /* buf wraps the current memory block for allocation requests. */
+ nghttp3_buf buf;
+} nghttp3_balloc;
+
+/*
+ * nghttp3_balloc_init initializes |balloc| with |blklen| which is the
+ * size of memory block.
+ */
+void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_balloc_free releases all allocated memory blocks.
+ */
+void nghttp3_balloc_free(nghttp3_balloc *balloc);
+
+/*
+ * nghttp3_balloc_get allocates |n| bytes of memory and assigns its
+ * pointer to |*pbuf|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n);
+
+/*
+ * nghttp3_balloc_clear releases all allocated memory blocks and
+ * initializes its state.
+ */
+void nghttp3_balloc_clear(nghttp3_balloc *balloc);
+
+#endif /* NGHTTP3_BALLOC_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c
index 2f9ce7b10ca..1fbb72c98af 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c
@@ -34,6 +34,10 @@
#include "nghttp3_conv.h"
#include "nghttp3_http.h"
+/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the
+ dynamic table capacity that QPACK encoder is willing to use. */
+#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096
+
/*
* conn_remote_stream_uni returns nonzero if |stream_id| is remote
* unidirectional stream ID.
@@ -62,15 +66,16 @@ static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) {
return 0;
}
-static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream) {
+static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream,
+ int fin) {
int rv;
if (!conn->callbacks.end_headers) {
return 0;
}
- rv = conn->callbacks.end_headers(conn, stream->node.nid.id, conn->user_data,
- stream->user_data);
+ rv = conn->callbacks.end_headers(conn, stream->node.nid.id, fin,
+ conn->user_data, stream->user_data);
if (rv != 0) {
/* TODO Allow ignore headers */
return NGHTTP3_ERR_CALLBACK_FAILURE;
@@ -97,52 +102,16 @@ static int conn_call_begin_trailers(nghttp3_conn *conn,
return 0;
}
-static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream) {
+static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream,
+ int fin) {
int rv;
if (!conn->callbacks.end_trailers) {
return 0;
}
- rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, conn->user_data,
- stream->user_data);
- if (rv != 0) {
- /* TODO Allow ignore headers */
- return NGHTTP3_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int conn_call_begin_push_promise(nghttp3_conn *conn,
- nghttp3_stream *stream,
- int64_t push_id) {
- int rv;
-
- if (!conn->callbacks.begin_push_promise) {
- return 0;
- }
-
- rv = conn->callbacks.begin_push_promise(conn, stream->node.nid.id, push_id,
- conn->user_data, stream->user_data);
- if (rv != 0) {
- /* TODO Allow ignore headers */
- return NGHTTP3_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int conn_call_end_push_promise(nghttp3_conn *conn,
- nghttp3_stream *stream, int64_t push_id) {
- int rv;
-
- if (!conn->callbacks.end_push_promise) {
- return 0;
- }
-
- rv = conn->callbacks.end_push_promise(conn, stream->node.nid.id, push_id,
- conn->user_data, stream->user_data);
+ rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, fin,
+ conn->user_data, stream->user_data);
if (rv != 0) {
/* TODO Allow ignore headers */
return NGHTTP3_ERR_CALLBACK_FAILURE;
@@ -167,36 +136,16 @@ static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
return 0;
}
-static int conn_call_cancel_push(nghttp3_conn *conn, int64_t push_id,
- nghttp3_stream *stream) {
- int rv;
-
- if (!conn->callbacks.cancel_push) {
- return 0;
- }
-
- rv = conn->callbacks.cancel_push(
- conn, push_id, stream ? stream->node.nid.id : -1, conn->user_data,
- stream ? stream->user_data : NULL);
- if (rv != 0) {
- return NGHTTP3_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int conn_call_send_stop_sending(nghttp3_conn *conn,
- nghttp3_stream *stream,
- uint64_t app_error_code) {
+static int conn_call_stop_sending(nghttp3_conn *conn, nghttp3_stream *stream,
+ uint64_t app_error_code) {
int rv;
- if (!conn->callbacks.send_stop_sending) {
+ if (!conn->callbacks.stop_sending) {
return 0;
}
- rv = conn->callbacks.send_stop_sending(conn, stream->node.nid.id,
- app_error_code, conn->user_data,
- stream->user_data);
+ rv = conn->callbacks.stop_sending(conn, stream->node.nid.id, app_error_code,
+ conn->user_data, stream->user_data);
if (rv != 0) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
@@ -221,23 +170,6 @@ static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream,
return 0;
}
-static int conn_call_push_stream(nghttp3_conn *conn, int64_t push_id,
- nghttp3_stream *stream) {
- int rv;
-
- if (!conn->callbacks.push_stream) {
- return 0;
- }
-
- rv = conn->callbacks.push_stream(conn, push_id, stream->node.nid.id,
- conn->user_data);
- if (rv != 0) {
- return NGHTTP3_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
static int conn_call_deferred_consume(nghttp3_conn *conn,
nghttp3_stream *stream,
size_t nconsumed) {
@@ -278,37 +210,40 @@ static int cycle_less(const nghttp3_pq_entry *lhsx,
return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP;
}
-static int conn_new(nghttp3_conn **pconn, int server,
- const nghttp3_callbacks *callbacks,
+static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
+ const nghttp3_callbacks *callbacks, int settings_version,
const nghttp3_settings *settings, const nghttp3_mem *mem,
void *user_data) {
int rv;
nghttp3_conn *conn;
size_t i;
+ (void)callbacks_version;
+ (void)settings_version;
+
+ if (mem == NULL) {
+ mem = nghttp3_mem_default();
+ }
conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn));
if (conn == NULL) {
return NGHTTP3_ERR_NOMEM;
}
- rv = nghttp3_map_init(&conn->streams, mem);
- if (rv != 0) {
- goto streams_init_fail;
- }
+ nghttp3_objalloc_init(&conn->out_chunk_objalloc,
+ NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem);
+ nghttp3_objalloc_stream_init(&conn->stream_objalloc, 64, mem);
- rv = nghttp3_map_init(&conn->pushes, mem);
- if (rv != 0) {
- goto pushes_init_fail;
- }
+ nghttp3_map_init(&conn->streams, mem);
rv = nghttp3_qpack_decoder_init(&conn->qdec,
- settings->qpack_max_table_capacity,
+ settings->qpack_max_dtable_capacity,
settings->qpack_blocked_streams, mem);
if (rv != 0) {
goto qdec_init_fail;
}
- rv = nghttp3_qpack_encoder_init(&conn->qenc, 0, 0, mem);
+ rv = nghttp3_qpack_encoder_init(
+ &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem);
if (rv != 0) {
goto qenc_init_fail;
}
@@ -319,18 +254,13 @@ static int conn_new(nghttp3_conn **pconn, int server,
nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem);
}
- rv = nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem);
- if (rv != 0) {
- goto remote_bidi_idtr_init_fail;
- }
-
- rv = nghttp3_gaptr_init(&conn->remote.uni.push_idtr, mem);
- if (rv != 0) {
- goto push_idtr_init_fail;
- }
+ nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem);
conn->callbacks = *callbacks;
conn->local.settings = *settings;
+ if (!server) {
+ conn->local.settings.enable_connect_protocol = 0;
+ }
nghttp3_settings_default(&conn->remote.settings);
conn->mem = mem;
conn->user_data = user_data;
@@ -338,52 +268,50 @@ static int conn_new(nghttp3_conn **pconn, int server,
conn->server = server;
conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1;
conn->tx.goaway_id = NGHTTP3_VARINT_MAX + 1;
- conn->rx.max_stream_id_bidi = 0;
- conn->rx.max_push_id = 0;
+ conn->rx.max_stream_id_bidi = -4;
*pconn = conn;
return 0;
-push_idtr_init_fail:
- nghttp3_idtr_free(&conn->remote.bidi.idtr);
-remote_bidi_idtr_init_fail:
- nghttp3_qpack_encoder_free(&conn->qenc);
qenc_init_fail:
nghttp3_qpack_decoder_free(&conn->qdec);
qdec_init_fail:
- nghttp3_map_free(&conn->pushes);
-pushes_init_fail:
nghttp3_map_free(&conn->streams);
-streams_init_fail:
+ nghttp3_objalloc_free(&conn->stream_objalloc);
+ nghttp3_objalloc_free(&conn->out_chunk_objalloc);
nghttp3_mem_free(mem, conn);
return rv;
}
-int nghttp3_conn_client_new(nghttp3_conn **pconn,
- const nghttp3_callbacks *callbacks,
- const nghttp3_settings *settings,
- const nghttp3_mem *mem, void *user_data) {
+int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn,
+ int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *user_data) {
int rv;
- rv = conn_new(pconn, /* server = */ 0, callbacks, settings, mem, user_data);
+ rv = conn_new(pconn, /* server = */ 0, callbacks_version, callbacks,
+ settings_version, settings, mem, user_data);
if (rv != 0) {
return rv;
}
- (*pconn)->remote.uni.unsent_max_pushes = settings->max_pushes;
-
return 0;
}
-int nghttp3_conn_server_new(nghttp3_conn **pconn,
- const nghttp3_callbacks *callbacks,
- const nghttp3_settings *settings,
- const nghttp3_mem *mem, void *user_data) {
+int nghttp3_conn_server_new_versioned(nghttp3_conn **pconn,
+ int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *user_data) {
int rv;
- rv = conn_new(pconn, /* server = */ 1, callbacks, settings, mem, user_data);
+ rv = conn_new(pconn, /* server = */ 1, callbacks_version, callbacks,
+ settings_version, settings, mem, user_data);
if (rv != 0) {
return rv;
}
@@ -391,17 +319,8 @@ int nghttp3_conn_server_new(nghttp3_conn **pconn,
return 0;
}
-static int free_push_promise(nghttp3_map_entry *ent, void *ptr) {
- nghttp3_push_promise *pp = nghttp3_struct_of(ent, nghttp3_push_promise, me);
- const nghttp3_mem *mem = ptr;
-
- nghttp3_push_promise_del(pp, mem);
-
- return 0;
-}
-
-static int free_stream(nghttp3_map_entry *ent, void *ptr) {
- nghttp3_stream *stream = nghttp3_struct_of(ent, nghttp3_stream, me);
+static int free_stream(void *data, void *ptr) {
+ nghttp3_stream *stream = data;
(void)ptr;
@@ -420,8 +339,6 @@ void nghttp3_conn_del(nghttp3_conn *conn) {
nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem);
nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem);
- nghttp3_gaptr_free(&conn->remote.uni.push_idtr);
-
nghttp3_idtr_free(&conn->remote.bidi.idtr);
for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
@@ -433,15 +350,30 @@ void nghttp3_conn_del(nghttp3_conn *conn) {
nghttp3_qpack_encoder_free(&conn->qenc);
nghttp3_qpack_decoder_free(&conn->qdec);
- nghttp3_map_each_free(&conn->pushes, free_push_promise, (void *)conn->mem);
- nghttp3_map_free(&conn->pushes);
-
nghttp3_map_each_free(&conn->streams, free_stream, NULL);
nghttp3_map_free(&conn->streams);
+ nghttp3_objalloc_free(&conn->stream_objalloc);
+ nghttp3_objalloc_free(&conn->out_chunk_objalloc);
+
nghttp3_mem_free(conn->mem, conn);
}
+static int conn_bidi_idtr_open(nghttp3_conn *conn, int64_t stream_id) {
+ int rv;
+
+ rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (nghttp3_ksl_len(&conn->remote.bidi.idtr.gap.gap) > 32) {
+ nghttp3_gaptr_drop_first_gap(&conn->remote.bidi.idtr.gap);
+ }
+
+ return 0;
+}
+
nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
const uint8_t *src, size_t srclen,
int fin) {
@@ -455,8 +387,18 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
/* QUIC transport ensures that this is new stream. */
if (conn->server) {
if (nghttp3_client_stream_bidi(stream_id)) {
- rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id);
- assert(rv == 0);
+ rv = conn_bidi_idtr_open(conn, stream_id);
+ if (rv != 0) {
+ if (nghttp3_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ /* Ignore return value. We might drop the first gap if there
+ are many gaps if QUIC stack allows too many holes in stream
+ ID space. idtr is used to decide whether PRIORITY_UPDATE
+ frame should be ignored or not and the frame is optional.
+ Ignoring them causes no harm. */
+ }
conn->rx.max_stream_id_bidi =
nghttp3_max(conn->rx.max_stream_id_bidi, stream_id);
@@ -566,12 +508,7 @@ static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream,
rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE;
break;
case NGHTTP3_STREAM_TYPE_PUSH:
- if (conn->server) {
- return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
- }
- stream->type = NGHTTP3_STREAM_TYPE_PUSH;
- rstate->state = NGHTTP3_PUSH_STREAM_STATE_PUSH_ID;
- break;
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
case NGHTTP3_STREAM_TYPE_QPACK_ENCODER:
if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) {
return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
@@ -603,7 +540,6 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
int fin) {
nghttp3_ssize nread = 0;
nghttp3_ssize nconsumed = 0;
- size_t push_nproc;
int rv;
assert(srclen || fin);
@@ -646,13 +582,6 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
}
nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen);
break;
- case NGHTTP3_STREAM_TYPE_PUSH:
- if (fin) {
- stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF;
- }
- nconsumed =
- nghttp3_conn_read_push(conn, &push_nproc, stream, src, srclen, fin);
- break;
case NGHTTP3_STREAM_TYPE_QPACK_ENCODER:
if (fin) {
return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
@@ -668,8 +597,7 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
case NGHTTP3_STREAM_TYPE_UNKNOWN:
nconsumed = (nghttp3_ssize)srclen;
- rv = conn_call_send_stop_sending(conn, stream,
- NGHTTP3_H3_STREAM_CREATION_ERROR);
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR);
if (rv != 0) {
return rv;
}
@@ -701,6 +629,8 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
size_t nconsumed = 0;
int busy = 0;
size_t len;
+ const uint8_t *pri_field_value = NULL;
+ size_t pri_field_valuelen = 0;
assert(srclen);
@@ -753,12 +683,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
}
switch (rstate->fr.hd.type) {
- case NGHTTP3_FRAME_CANCEL_PUSH:
- if (rstate->left == 0) {
- return NGHTTP3_ERR_H3_FRAME_ERROR;
- }
- rstate->state = NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH;
- break;
case NGHTTP3_FRAME_SETTINGS:
/* SETTINGS frame might be empty. */
if (rstate->left == 0) {
@@ -782,6 +706,19 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
}
rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID;
break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ if (!conn->server) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID;
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
+ /* We do not support push */
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */
case NGHTTP3_FRAME_DATA:
case NGHTTP3_FRAME_HEADERS:
case NGHTTP3_FRAME_PUSH_PROMISE:
@@ -797,34 +734,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
break;
}
break;
- case NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH:
- len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- assert(len > 0);
- nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
- if (nread < 0) {
- return NGHTTP3_ERR_H3_FRAME_ERROR;
- }
-
- p += nread;
- nconsumed += (size_t)nread;
- rstate->left -= nread;
- if (rvint->left) {
- return (nghttp3_ssize)nconsumed;
- }
- rstate->fr.cancel_push.push_id = rvint->acc;
- nghttp3_varint_read_state_reset(rvint);
-
- if (conn->server) {
- rv = nghttp3_conn_on_server_cancel_push(conn, &rstate->fr.cancel_push);
- } else {
- rv = nghttp3_conn_on_client_cancel_push(conn, &rstate->fr.cancel_push);
- }
- if (rv != 0) {
- return rv;
- }
-
- nghttp3_stream_read_state_reset(rstate);
- break;
case NGHTTP3_CTRL_STREAM_STATE_SETTINGS:
for (; p != end;) {
if (rstate->left == 0) {
@@ -953,7 +862,7 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
return (nghttp3_ssize)nconsumed;
}
- if (conn->server && !nghttp3_client_stream_bidi(rvint->acc)) {
+ if (!conn->server && !nghttp3_client_stream_bidi(rvint->acc)) {
return NGHTTP3_ERR_H3_ID_ERROR;
}
if (conn->rx.goaway_id < rvint->acc) {
@@ -964,6 +873,14 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
conn->rx.goaway_id = rvint->acc;
nghttp3_varint_read_state_reset(rvint);
+ if (conn->callbacks.shutdown) {
+ rv =
+ conn->callbacks.shutdown(conn, conn->rx.goaway_id, conn->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+
nghttp3_stream_read_state_reset(rstate);
break;
case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID:
@@ -982,373 +899,139 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
return (nghttp3_ssize)nconsumed;
}
- if (conn->local.uni.max_pushes > (uint64_t)rvint->acc) {
+ if (conn->local.uni.max_pushes > (uint64_t)rvint->acc + 1) {
return NGHTTP3_ERR_H3_FRAME_ERROR;
}
- conn->local.uni.max_pushes = (uint64_t)rvint->acc;
+ conn->local.uni.max_pushes = (uint64_t)rvint->acc + 1;
nghttp3_varint_read_state_reset(rvint);
nghttp3_stream_read_state_reset(rstate);
break;
- case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME:
+ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID:
+ /* server side only */
len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- p += len;
- nconsumed += len;
- rstate->left -= (int64_t)len;
-
- if (rstate->left) {
- return (nghttp3_ssize)nconsumed;
- }
-
- nghttp3_stream_read_state_reset(rstate);
- break;
- default:
- /* unreachable */
- assert(0);
- }
- }
-
- return (nghttp3_ssize)nconsumed;
-}
-
-nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc,
- nghttp3_stream *stream, const uint8_t *src,
- size_t srclen, int fin) {
- const uint8_t *p = src, *end = src ? src + srclen : src;
- int rv;
- nghttp3_stream_read_state *rstate = &stream->rstate;
- nghttp3_varint_read_state *rvint = &rstate->rvint;
- nghttp3_ssize nread;
- size_t nconsumed = 0;
- int busy = 0;
- size_t len;
- int64_t push_id;
-
- if (stream->flags & (NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED |
- NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED)) {
- *pnproc = 0;
-
- if (srclen == 0) {
- return 0;
- }
-
- rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
- if (rv != 0) {
- return rv;
- }
- return 0;
- }
-
- for (; p != end || busy;) {
- busy = 0;
- switch (rstate->state) {
- case NGHTTP3_PUSH_STREAM_STATE_PUSH_ID:
- assert(end - p > 0);
- nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin);
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
if (nread < 0) {
- return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
}
p += nread;
nconsumed += (size_t)nread;
+ rstate->left -= nread;
if (rvint->left) {
- goto almost_done;
- }
-
- push_id = rvint->acc;
- nghttp3_varint_read_state_reset(rvint);
-
- rv = nghttp3_conn_on_stream_push_id(conn, stream, push_id);
- if (rv != 0) {
- if (rv == NGHTTP3_ERR_IGNORE_STREAM) {
- rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_REST;
- break;
- }
- return (nghttp3_ssize)rv;
- }
-
- rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE;
-
- if (stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED) {
- if (p != end) {
- rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
- if (rv != 0) {
- return rv;
- }
- }
- *pnproc = (size_t)(p - src);
return (nghttp3_ssize)nconsumed;
}
- if (end == p) {
- goto almost_done;
- }
-
- /* Fall through */
- case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE:
- assert(end - p > 0);
- nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin);
- if (nread < 0) {
- return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
- }
-
- p += nread;
- nconsumed += (size_t)nread;
- if (rvint->left) {
- goto almost_done;
- }
-
- rstate->fr.hd.type = rvint->acc;
+ rstate->fr.priority_update.pri_elem_id = rvint->acc;
nghttp3_varint_read_state_reset(rvint);
- rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH;
- if (p == end) {
- goto almost_done;
- }
- /* Fall through */
- case NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH:
- assert(end - p > 0);
- nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin);
- if (nread < 0) {
- return NGHTTP3_ERR_H3_FRAME_ERROR;
- }
- p += nread;
- nconsumed += (size_t)nread;
- if (rvint->left) {
- goto almost_done;
- }
-
- rstate->left = rstate->fr.hd.length = rvint->acc;
- nghttp3_varint_read_state_reset(rvint);
+ if (rstate->left == 0) {
+ rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY;
+ rstate->fr.priority_update.pri.inc = 0;
- switch (rstate->fr.hd.type) {
- case NGHTTP3_FRAME_DATA:
- rv = nghttp3_stream_transit_rx_http_state(
- stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN);
+ rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update);
if (rv != 0) {
return rv;
}
- /* DATA frame might be empty. */
- if (rstate->left == 0) {
- rv = nghttp3_stream_transit_rx_http_state(
- stream, NGHTTP3_HTTP_EVENT_DATA_END);
- assert(0 == rv);
- nghttp3_stream_read_state_reset(rstate);
- break;
- }
- rstate->state = NGHTTP3_PUSH_STREAM_STATE_DATA;
+ nghttp3_stream_read_state_reset(rstate);
break;
- case NGHTTP3_FRAME_HEADERS:
- rv = nghttp3_stream_transit_rx_http_state(
- stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN);
- if (rv != 0) {
- return rv;
- }
- if (rstate->left == 0) {
- rv = nghttp3_stream_empty_headers_allowed(stream);
- if (rv != 0) {
- return rv;
- }
-
- rv = nghttp3_stream_transit_rx_http_state(
- stream, NGHTTP3_HTTP_EVENT_HEADERS_END);
- assert(0 == rv);
+ }
- nghttp3_stream_read_state_reset(rstate);
- break;
- }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE;
- switch (stream->rx.hstate) {
- case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
- rv = conn_call_begin_headers(conn, stream);
- break;
- case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
- rv = conn_call_begin_trailers(conn, stream);
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE:
+ /* We need to buffer Priority Field Value because it might be
+ fragmented. */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ if (conn->rx.pri_fieldbuflen == 0 && rstate->left == (int64_t)len) {
+ /* Everything is in the input buffer. Apply same length
+ limit we impose when buffering the field. */
+ if (len > sizeof(conn->rx.pri_fieldbuf)) {
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
break;
- default:
- /* Unreachable */
- assert(0);
- }
-
- if (rv != 0) {
- return rv;
}
- rstate->state = NGHTTP3_PUSH_STREAM_STATE_HEADERS;
- break;
- case NGHTTP3_FRAME_PUSH_PROMISE:
- case NGHTTP3_FRAME_CANCEL_PUSH:
- case NGHTTP3_FRAME_SETTINGS:
- case NGHTTP3_FRAME_GOAWAY:
- case NGHTTP3_FRAME_MAX_PUSH_ID:
- case NGHTTP3_H2_FRAME_PRIORITY:
- case NGHTTP3_H2_FRAME_PING:
- case NGHTTP3_H2_FRAME_WINDOW_UPDATE:
- case NGHTTP3_H2_FRAME_CONTINUATION:
- return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
- default:
- /* TODO Handle reserved frame type */
+ pri_field_value = p;
+ pri_field_valuelen = len;
+ } else if (len + conn->rx.pri_fieldbuflen >
+ sizeof(conn->rx.pri_fieldbuf)) {
busy = 1;
- rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
break;
- }
- break;
- case NGHTTP3_PUSH_STREAM_STATE_DATA:
- len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- rv = nghttp3_conn_on_data(conn, stream, p, len);
- if (rv != 0) {
- return rv;
+ } else {
+ memcpy(conn->rx.pri_fieldbuf + conn->rx.pri_fieldbuflen, p, len);
+ conn->rx.pri_fieldbuflen += len;
+
+ if (rstate->left == (int64_t)len) {
+ pri_field_value = conn->rx.pri_fieldbuf;
+ pri_field_valuelen = conn->rx.pri_fieldbuflen;
+ }
}
p += len;
+ nconsumed += len;
rstate->left -= (int64_t)len;
if (rstate->left) {
- goto almost_done;
- }
-
- rv = nghttp3_stream_transit_rx_http_state(stream,
- NGHTTP3_HTTP_EVENT_DATA_END);
- assert(0 == rv);
-
- nghttp3_stream_read_state_reset(rstate);
- break;
- case NGHTTP3_PUSH_STREAM_STATE_HEADERS:
- len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len,
- (int64_t)len == rstate->left);
- if (nread < 0) {
- return nread;
- }
-
- p += nread;
- nconsumed += (size_t)nread;
- rstate->left -= nread;
-
- if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
- if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) {
- rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
- if (rv != 0) {
- return rv;
- }
- }
- *pnproc = (size_t)(p - src);
return (nghttp3_ssize)nconsumed;
}
- if (rstate->left) {
- goto almost_done;
- }
-
- switch (stream->rx.hstate) {
- case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
- rv = nghttp3_http_on_response_headers(&stream->rx.http);
- if (rv != 0) {
- return rv;
- }
+ rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY;
+ rstate->fr.priority_update.pri.inc = 0;
- rv = conn_call_end_headers(conn, stream);
- break;
- case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
- rv = conn_call_end_trailers(conn, stream);
- break;
- default:
- /* Unreachable */
- assert(0);
+ if (nghttp3_http_parse_priority(&rstate->fr.priority_update.pri,
+ pri_field_value,
+ pri_field_valuelen) != 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
}
+ rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update);
if (rv != 0) {
return rv;
}
- rv = nghttp3_stream_transit_rx_http_state(stream,
- NGHTTP3_HTTP_EVENT_HEADERS_END);
- assert(0 == rv);
+ conn->rx.pri_fieldbuflen = 0;
nghttp3_stream_read_state_reset(rstate);
break;
- case NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME:
+ case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME:
len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
p += len;
nconsumed += len;
rstate->left -= (int64_t)len;
if (rstate->left) {
- goto almost_done;
+ return (nghttp3_ssize)nconsumed;
}
nghttp3_stream_read_state_reset(rstate);
break;
- case NGHTTP3_PUSH_STREAM_STATE_IGN_REST:
- nconsumed += (size_t)(end - p);
- *pnproc = (size_t)(p - src);
- return (nghttp3_ssize)nconsumed;
- }
- }
-
-almost_done:
- if (fin) {
- switch (rstate->state) {
- case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE:
- if (rvint->left) {
- return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
- }
- rv = nghttp3_stream_transit_rx_http_state(stream,
- NGHTTP3_HTTP_EVENT_MSG_END);
- if (rv != 0) {
- return rv;
- }
- rv = conn_call_end_stream(conn, stream);
- if (rv != 0) {
- return rv;
- }
- break;
- case NGHTTP3_PUSH_STREAM_STATE_IGN_REST:
- break;
default:
- return NGHTTP3_ERR_H3_FRAME_ERROR;
+ /* unreachable */
+ assert(0);
}
}
- *pnproc = (size_t)(p - src);
return (nghttp3_ssize)nconsumed;
}
-static void conn_delete_push_promise(nghttp3_conn *conn,
- nghttp3_push_promise *pp) {
- int rv;
-
- rv = nghttp3_map_remove(&conn->pushes, (key_type)pp->node.nid.id);
- assert(0 == rv);
-
- if (!conn->server &&
- !(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED)) {
- ++conn->remote.uni.unsent_max_pushes;
- }
-
- nghttp3_push_promise_del(pp, conn->mem);
-}
-
static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
- int bidi_or_push = nghttp3_stream_bidi_or_push(stream);
+ int bidi = nghttp3_client_stream_bidi(stream->node.nid.id);
int rv;
- if (bidi_or_push) {
- rv = nghttp3_http_on_remote_end_stream(stream);
- if (rv != 0) {
- return rv;
- }
- }
-
rv = conn_call_deferred_consume(conn, stream,
nghttp3_stream_get_buffered_datalen(stream));
if (rv != 0) {
return rv;
}
- if (bidi_or_push && conn->callbacks.stream_close) {
+ if (bidi && conn->callbacks.stream_close) {
rv = conn->callbacks.stream_close(conn, stream->node.nid.id,
stream->error_code, conn->user_data,
stream->user_data);
@@ -1357,16 +1040,11 @@ static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
}
}
- rv = nghttp3_map_remove(&conn->streams, (key_type)stream->node.nid.id);
+ rv = nghttp3_map_remove(&conn->streams,
+ (nghttp3_map_key_type)stream->node.nid.id);
assert(0 == rv);
- if (stream->pp) {
- assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH);
-
- conn_delete_push_promise(conn, stream->pp);
- }
-
nghttp3_stream_del(stream);
return 0;
@@ -1380,21 +1058,19 @@ static int conn_process_blocked_stream_data(nghttp3_conn *conn,
int rv;
size_t len;
+ assert(nghttp3_client_stream_bidi(stream->node.nid.id));
+
for (;;) {
len = nghttp3_ringbuf_len(&stream->inq);
if (len == 0) {
break;
}
+
buf = nghttp3_ringbuf_get(&stream->inq, 0);
- if (nghttp3_stream_uni(stream->node.nid.id)) {
- nconsumed = nghttp3_conn_read_push(
- conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf),
- len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF));
- } else {
- nconsumed = nghttp3_conn_read_bidi(
- conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf),
- len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF));
- }
+
+ nconsumed = nghttp3_conn_read_bidi(
+ conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf),
+ len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF));
if (nconsumed < 0) {
return (int)nconsumed;
}
@@ -1468,8 +1144,14 @@ nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen);
}
+static nghttp3_tnode *stream_get_sched_node(nghttp3_stream *stream) {
+ return &stream->node;
+}
+
static int conn_update_stream_priority(nghttp3_conn *conn,
nghttp3_stream *stream, uint8_t pri) {
+ assert(nghttp3_client_stream_bidi(stream->node.nid.id));
+
if (stream->node.pri == pri) {
return 0;
}
@@ -1478,8 +1160,6 @@ static int conn_update_stream_priority(nghttp3_conn *conn,
stream->node.pri = pri;
- assert(nghttp3_stream_bidi_or_push(stream));
-
if (nghttp3_stream_require_schedule(stream)) {
return nghttp3_conn_schedule_stream(conn, stream);
}
@@ -1498,10 +1178,12 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
size_t nconsumed = 0;
int busy = 0;
size_t len;
- nghttp3_push_promise *pp;
- nghttp3_push_promise fake_pp = {{0}, {{0}, 0, {0}, 0, 0, 0}, {0}, NULL, -1,
- 0};
- nghttp3_frame_entry frent;
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) {
+ *pnproc = srclen;
+
+ return (nghttp3_ssize)srclen;
+ }
if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
*pnproc = 0;
@@ -1614,23 +1296,13 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS;
break;
- case NGHTTP3_FRAME_PUSH_PROMISE:
- if (conn->server) {
- return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
- }
-
- if (rstate->left == 0) {
- return NGHTTP3_ERR_H3_FRAME_ERROR;
- }
-
- /* No stream->rx.hstate change with PUSH_PROMISE */
-
- rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID;
- break;
+ case NGHTTP3_FRAME_PUSH_PROMISE: /* We do not support push */
case NGHTTP3_FRAME_CANCEL_PUSH:
case NGHTTP3_FRAME_SETTINGS:
case NGHTTP3_FRAME_GOAWAY:
case NGHTTP3_FRAME_MAX_PUSH_ID:
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
case NGHTTP3_H2_FRAME_PRIORITY:
case NGHTTP3_H2_FRAME_PING:
case NGHTTP3_H2_FRAME_WINDOW_UPDATE:
@@ -1662,199 +1334,15 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
nghttp3_stream_read_state_reset(rstate);
break;
- case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID:
- len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- nread = nghttp3_read_varint(rvint, p, (size_t)(end - p),
- (int64_t)len == rstate->left);
- if (nread < 0) {
- return NGHTTP3_ERR_H3_FRAME_ERROR;
- }
-
- p += nread;
- nconsumed += (size_t)nread;
- rstate->left -= nread;
- if (rvint->left) {
- goto almost_done;
- }
-
- rstate->fr.push_promise.push_id = rvint->acc;
- nghttp3_varint_read_state_reset(rvint);
-
- if (rstate->left == 0) {
- return NGHTTP3_ERR_H3_FRAME_ERROR;
- }
-
- rv = nghttp3_conn_on_push_promise_push_id(
- conn, rstate->fr.push_promise.push_id, stream);
- if (rv != 0) {
- if (rv == NGHTTP3_ERR_IGNORE_PUSH_PROMISE) {
- rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE;
- if (p == end) {
- goto almost_done;
- }
- break;
- }
-
- return rv;
- }
-
- rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE;
-
- if (p == end) {
- goto almost_done;
- }
- /* Fall through */
- case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE:
- pp =
- nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id);
-
- assert(pp);
-
- len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- nread = nghttp3_conn_on_headers(conn, stream, pp, p, len,
- (int64_t)len == rstate->left);
- if (nread < 0) {
- return nread;
- }
-
- p += nread;
- nconsumed += (size_t)nread;
- rstate->left -= nread;
-
- if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
- if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) {
- rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
- if (rv != 0) {
- return rv;
- }
- }
- *pnproc = (size_t)(p - src);
- return (nghttp3_ssize)nconsumed;
- }
-
- if (rstate->left) {
- goto almost_done;
- }
-
- rv = nghttp3_http_on_request_headers(&pp->http);
- if (rv != 0) {
- return rv;
- }
-
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED;
-
- rv = conn_call_end_push_promise(conn, stream, pp->node.nid.id);
- if (rv != 0) {
- return rv;
- }
-
- /* Find pp again because application might call
- nghttp3_conn_cancel_push and it may delete pp. */
- pp =
- nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id);
- if (!pp) {
- nghttp3_stream_read_state_reset(rstate);
- break;
- }
-
- if (!pp->stream && (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED)) {
- if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL) {
- rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream);
- if (rv != 0) {
- return rv;
- }
- }
-
- conn_delete_push_promise(conn, pp);
-
- nghttp3_stream_read_state_reset(rstate);
- break;
- }
-
- if (pp->stream) {
- ++conn->remote.uni.unsent_max_pushes;
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED;
-
- if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) {
- assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED);
-
- rv = conn_call_push_stream(conn, pp->node.nid.id, pp->stream);
- if (rv != 0) {
- return rv;
- }
-
- pp->stream->flags &=
- (uint16_t)~NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED;
-
- rv = conn_process_blocked_stream_data(conn, pp->stream);
- if (rv != 0) {
- return rv;
- }
- }
- }
-
- nghttp3_stream_read_state_reset(rstate);
- break;
- case NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE:
+ case NGHTTP3_REQ_STREAM_STATE_HEADERS:
len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- nread = nghttp3_conn_on_headers(conn, stream, &fake_pp, p, len,
+ nread = nghttp3_conn_on_headers(conn, stream, p, len,
(int64_t)len == rstate->left);
if (nread < 0) {
- return nread;
- }
-
- p += nread;
- nconsumed += (size_t)nread;
- rstate->left -= nread;
-
- if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
- if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) {
- rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
- if (rv != 0) {
- return rv;
- }
- }
- *pnproc = (size_t)(p - src);
- return (nghttp3_ssize)nconsumed;
- }
-
- if (rstate->left) {
- goto almost_done;
- }
-
- pp =
- nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id);
- if (pp) {
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED;
-
- if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
- conn->tx.goaway_id <= pp->node.nid.id) {
- if (pp->stream) {
- rv = nghttp3_conn_reject_push_stream(conn, pp->stream);
- if (rv != 0) {
- return rv;
- }
- } else {
- frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH;
- frent.fr.cancel_push.push_id = pp->node.nid.id;
-
- rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
- if (rv != 0) {
- return rv;
- }
-
- conn_delete_push_promise(conn, pp);
- }
+ if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
+ goto http_header_error;
}
- }
- nghttp3_stream_read_state_reset(rstate);
- break;
- case NGHTTP3_REQ_STREAM_STATE_HEADERS:
- len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
- nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len,
- (int64_t)len == rstate->left);
- if (nread < 0) {
return nread;
}
@@ -1891,9 +1379,14 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
default:
/* Unreachable */
assert(0);
+ abort();
}
if (rv != 0) {
+ if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
+ goto http_header_error;
+ }
+
return rv;
}
@@ -1901,7 +1394,10 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
/* Only server utilizes priority information to schedule
streams. */
- if (conn->server) {
+ if (conn->server &&
+ (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_PRIORITY) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET)) {
rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri);
if (rv != 0) {
return rv;
@@ -1909,11 +1405,11 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
}
/* fall through */
case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
- rv = conn_call_end_headers(conn, stream);
+ rv = conn_call_end_headers(conn, stream, p == end && fin);
break;
case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
- rv = conn_call_end_trailers(conn, stream);
+ rv = conn_call_end_trailers(conn, stream, p == end && fin);
break;
default:
/* Unreachable */
@@ -1929,6 +1425,25 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
assert(0 == rv);
nghttp3_stream_read_state_reset(rstate);
+
+ break;
+
+ http_header_error:
+ stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR;
+
+ busy = 1;
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST;
+
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+
break;
case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME:
len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
@@ -1944,7 +1459,7 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
break;
case NGHTTP3_REQ_STREAM_STATE_IGN_REST:
nconsumed += (size_t)(end - p);
- *pnproc = (size_t)(p - src);
+ *pnproc = (size_t)(end - src);
return (nghttp3_ssize)nconsumed;
}
}
@@ -1999,144 +1514,6 @@ int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
return 0;
}
-static int push_idtr_push(nghttp3_gaptr *push_idtr, int64_t push_id) {
- int rv;
-
- rv = nghttp3_gaptr_push(push_idtr, (uint64_t)push_id, 1);
- if (rv != 0) {
- return rv;
- }
-
- /* Server SHOULD use push ID sequentially, but even if it does, we
- might see gaps in push IDs because we might stop reading stream
- which ignores PUSH_PROMISE. In order to limit the number of
- gaps, drop earlier gaps if certain limit is reached. This makes
- otherwise valid push ignored.*/
- if (nghttp3_ksl_len(&push_idtr->gap) > 100) {
- nghttp3_gaptr_drop_first_gap(push_idtr);
- }
-
- return 0;
-}
-
-int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id,
- nghttp3_stream *stream) {
- int rv;
- nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr;
- nghttp3_push_promise *pp;
-
- if (conn->remote.uni.max_pushes <= (uint64_t)push_id) {
- return NGHTTP3_ERR_H3_ID_ERROR;
- }
-
- pp = nghttp3_conn_find_push_promise(conn, push_id);
- if (pp) {
- if ((pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_BOUND) ||
- (pp->stream_id != -1 && pp->stream_id != stream->node.nid.id)) {
- return NGHTTP3_ERR_IGNORE_PUSH_PROMISE;
- }
- if (pp->stream) {
- assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED);
- /* Push unidirectional stream has already been received and
- blocked */
- } else if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) {
- /* We will call begin_push_promise callback even if push is
- cancelled */
- } else {
- return NGHTTP3_ERR_H3_FRAME_ERROR;
- }
-
- assert(pp->stream_id == -1);
-
- pp->stream_id = stream->node.nid.id;
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND;
- } else if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)push_id, 1)) {
- return NGHTTP3_ERR_IGNORE_PUSH_PROMISE;
- } else {
- rv = push_idtr_push(push_idtr, push_id);
- if (rv != 0) {
- return rv;
- }
-
- rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node);
- if (rv != 0) {
- return rv;
- }
- }
-
- conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, push_id);
-
- if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
- conn->tx.goaway_id <= push_id) {
- return NGHTTP3_ERR_IGNORE_PUSH_PROMISE;
- }
-
- rv = conn_call_begin_push_promise(conn, stream, push_id);
- if (rv != 0) {
- return rv;
- }
-
- return 0;
-}
-
-int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn,
- const nghttp3_frame_cancel_push *fr) {
- nghttp3_push_promise *pp;
- nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr;
- int rv;
-
- if (conn->remote.uni.max_pushes <= (uint64_t)fr->push_id) {
- return NGHTTP3_ERR_H3_ID_ERROR;
- }
-
- pp = nghttp3_conn_find_push_promise(conn, fr->push_id);
- if (pp == NULL) {
- if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)fr->push_id, 1)) {
- /* push is already cancelled or server is misbehaving */
- return 0;
- }
-
- /* We have not received PUSH_PROMISE yet */
- rv = push_idtr_push(push_idtr, fr->push_id);
- if (rv != 0) {
- return rv;
- }
-
- conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, fr->push_id);
-
- rv = nghttp3_conn_create_push_promise(conn, &pp, fr->push_id, NULL);
- if (rv != 0) {
- return rv;
- }
-
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL;
-
- /* cancel_push callback will be called after PUSH_PROMISE frame is
- completely received. */
-
- return 0;
- }
-
- if (pp->stream) {
- return 0;
- }
-
- if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) {
- rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream);
- if (rv != 0) {
- return rv;
- }
-
- conn_delete_push_promise(conn, pp);
-
- return 0;
- }
-
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL;
-
- return 0;
-}
-
static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) {
uint32_t urgency = nghttp3_pri_uint8_urgency(tnode->pri);
@@ -2145,121 +1522,8 @@ static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) {
return &conn->sched[urgency].spq;
}
-int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn,
- const nghttp3_frame_cancel_push *fr) {
- nghttp3_push_promise *pp;
- nghttp3_stream *stream;
- int rv;
-
- if (conn->local.uni.next_push_id <= fr->push_id) {
- return NGHTTP3_ERR_H3_ID_ERROR;
- }
-
- pp = nghttp3_conn_find_push_promise(conn, fr->push_id);
- if (pp == NULL) {
- return 0;
- }
-
- stream = pp->stream;
-
- rv = conn_call_cancel_push(conn, fr->push_id, stream);
- if (rv != 0) {
- return rv;
- }
-
- if (stream) {
- rv = nghttp3_conn_close_stream(conn, stream->node.nid.id,
- NGHTTP3_H3_REQUEST_CANCELLED);
- if (rv != 0) {
- assert(NGHTTP3_ERR_STREAM_NOT_FOUND != rv);
- return rv;
- }
- return 0;
- }
-
- nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node));
-
- conn_delete_push_promise(conn, pp);
-
- return 0;
-}
-
-int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream,
- int64_t push_id) {
- nghttp3_push_promise *pp;
- int rv;
-
- if (nghttp3_gaptr_is_pushed(&conn->remote.uni.push_idtr, (uint64_t)push_id,
- 1)) {
- pp = nghttp3_conn_find_push_promise(conn, push_id);
- if (pp) {
- if (pp->stream) {
- return NGHTTP3_ERR_H3_ID_ERROR;
- }
- pp->stream = stream;
- stream->pp = pp;
-
- assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL));
-
- if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
- conn->tx.goaway_id <= push_id) {
- rv = nghttp3_conn_reject_push_stream(conn, stream);
- if (rv != 0) {
- return rv;
- }
- return NGHTTP3_ERR_IGNORE_STREAM;
- }
-
- if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) {
- ++conn->remote.uni.unsent_max_pushes;
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED;
-
- return conn_call_push_stream(conn, push_id, stream);
- }
-
- stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED;
-
- return 0;
- }
-
- /* Push ID has been received, but pp is gone. This means that
- push is cancelled or server is misbehaving. We have no
- information to distinguish the two, so just cancel QPACK stream
- just in case, and ask application to send STOP_SENDING and
- ignore all frames in this stream. */
- rv = nghttp3_conn_cancel_push_stream(conn, stream);
- if (rv != 0) {
- return rv;
- }
- return NGHTTP3_ERR_IGNORE_STREAM;
- }
-
- if (conn->remote.uni.max_pushes <= (uint64_t)push_id) {
- return NGHTTP3_ERR_H3_ID_ERROR;
- }
-
- rv = push_idtr_push(&conn->remote.uni.push_idtr, push_id);
- if (rv != 0) {
- return rv;
- }
-
- /* Don't know the associated stream of PUSH_PROMISE. It doesn't
- matter because client sends nothing to this stream. */
- rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, NULL);
- if (rv != 0) {
- return rv;
- }
-
- pp->stream = stream;
- stream->pp = pp;
- stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED;
-
- return 0;
-}
-
static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn,
nghttp3_stream *stream,
- nghttp3_push_promise *pp,
const uint8_t *src, size_t srclen,
int fin) {
nghttp3_ssize nread;
@@ -2272,37 +1536,26 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn,
nghttp3_http_state *http;
int request = 0;
int trailers = 0;
- int ignore_pp = 0;
- if (pp) {
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
request = 1;
- ignore_pp = pp->stream_id != stream->node.nid.id;
- if (ignore_pp) {
- http = NULL;
- } else {
- http = &pp->http;
- }
- } else {
- switch (stream->rx.hstate) {
- case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
- request = 1;
- /* Fall through */
- case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
- recv_header = conn->callbacks.recv_header;
- break;
- case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
- request = 1;
- /* Fall through */
- case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
- trailers = 1;
- recv_header = conn->callbacks.recv_trailer;
- break;
- default:
- /* Unreachable */
- assert(0);
- }
- http = &stream->rx.http;
+ /* Fall through */
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ recv_header = conn->callbacks.recv_header;
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ request = 1;
+ /* Fall through */
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ trailers = 1;
+ recv_header = conn->callbacks.recv_trailer;
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
}
+ http = &stream->rx.http;
nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen);
buf.last = buf.end;
@@ -2342,17 +1595,9 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn,
}
if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
- if (ignore_pp) {
- nghttp3_rcbuf_decref(nv.name);
- nghttp3_rcbuf_decref(nv.value);
-
- continue;
- }
-
- assert(http);
-
- rv = nghttp3_http_on_header(http, stream->rstate.fr.hd.type, &nv, request,
- trailers);
+ rv = nghttp3_http_on_header(
+ http, &nv, request, trailers,
+ conn->server && conn->local.settings.enable_connect_protocol);
switch (rv) {
case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
break;
@@ -2360,18 +1605,13 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn,
rv = 0;
break;
case 0:
- if (pp) {
- if (conn->callbacks.recv_push_promise) {
- rv = conn->callbacks.recv_push_promise(
- conn, stream->node.nid.id, pp->node.nid.id, nv.token, nv.name,
- nv.value, nv.flags, conn->user_data, stream->user_data);
- }
- break;
- }
if (recv_header) {
rv = recv_header(conn, stream->node.nid.id, nv.token, nv.name,
nv.value, nv.flags, conn->user_data,
stream->user_data);
+ if (rv != 0) {
+ rv = NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
}
break;
default:
@@ -2393,23 +1633,19 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn,
nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn,
nghttp3_stream *stream,
- nghttp3_push_promise *pp,
const uint8_t *src, size_t srclen,
int fin) {
if (srclen == 0 && !fin) {
return 0;
}
- return conn_decode_headers(conn, stream, pp, src, srclen, fin);
+ return conn_decode_headers(conn, stream, src, srclen, fin);
}
int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
const nghttp3_frame_settings *fr) {
const nghttp3_settings_entry *ent = &fr->iv[0];
nghttp3_settings *dest = &conn->remote.settings;
- int rv;
- size_t max_table_capacity = SIZE_MAX;
- size_t max_blocked_streams = SIZE_MAX;
/* TODO Check for duplicates */
switch (ent->id) {
@@ -2417,29 +1653,44 @@ int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
dest->max_field_section_size = ent->value;
break;
case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY:
- dest->qpack_max_table_capacity = (size_t)ent->value;
- max_table_capacity =
- nghttp3_min(max_table_capacity, dest->qpack_max_table_capacity);
- rv = nghttp3_qpack_encoder_set_hard_max_dtable_size(&conn->qenc,
- max_table_capacity);
- if (rv != 0) {
- return rv;
+ if (dest->qpack_max_dtable_capacity != 0) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
}
- rv = nghttp3_qpack_encoder_set_max_dtable_size(&conn->qenc,
- max_table_capacity);
- if (rv != 0) {
- return rv;
+
+ if (ent->value == 0) {
+ break;
}
+
+ dest->qpack_max_dtable_capacity = (size_t)ent->value;
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&conn->qenc,
+ (size_t)ent->value);
break;
case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS:
+ if (dest->qpack_blocked_streams != 0) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ if (ent->value == 0) {
+ break;
+ }
+
dest->qpack_blocked_streams = (size_t)ent->value;
- max_blocked_streams =
- nghttp3_min(max_blocked_streams, dest->qpack_blocked_streams);
- rv =
- nghttp3_qpack_encoder_set_max_blocked(&conn->qenc, max_blocked_streams);
- if (rv != 0) {
- return rv;
+
+ nghttp3_qpack_encoder_set_max_blocked_streams(
+ &conn->qenc, (size_t)nghttp3_min(100, ent->value));
+ break;
+ case NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL:
+ if (!conn->server) {
+ break;
+ }
+ if (ent->value != 0 && ent->value != 1) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
}
+ if (ent->value == 0 && dest->enable_connect_protocol) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+ dest->enable_connect_protocol = (int)ent->value;
break;
case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH:
case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS:
@@ -2454,8 +1705,71 @@ int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
return 0;
}
+static int
+conn_on_priority_update_stream(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr) {
+ int64_t stream_id = fr->pri_elem_id;
+ nghttp3_stream *stream;
+ int rv;
+
+ if (!nghttp3_client_stream_bidi(stream_id) ||
+ nghttp3_ord_stream_id(stream_id) > conn->remote.bidi.max_client_streams) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
+ conn->tx.goaway_id <= stream_id) {
+ /* Connection is going down. Ignore priority signal. */
+ return 0;
+ }
+
+ rv = conn_bidi_idtr_open(conn, stream_id);
+ if (rv != 0) {
+ if (nghttp3_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ assert(rv == NGHTTP3_ERR_STREAM_IN_USE);
+
+ /* The stream is gone. Just ignore. */
+ return 0;
+ }
+
+ conn->rx.max_stream_id_bidi =
+ nghttp3_max(conn->rx.max_stream_id_bidi, stream_id);
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->node.pri = nghttp3_pri_to_uint8(&fr->pri);
+ stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED;
+
+ return 0;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET) {
+ return 0;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED;
+
+ return conn_update_stream_priority(conn, stream,
+ nghttp3_pri_to_uint8(&fr->pri));
+}
+
+int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr) {
+ assert(conn->server);
+ assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE);
+
+ return conn_on_priority_update_stream(conn, fr);
+}
+
static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id,
- size_t datalen, void *user_data) {
+ uint64_t datalen, void *user_data) {
nghttp3_conn *conn = stream->conn;
int rv;
@@ -2481,6 +1795,7 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
};
rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks,
+ &conn->out_chunk_objalloc, &conn->stream_objalloc,
conn->mem);
if (rv != 0) {
return rv;
@@ -2488,7 +1803,8 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
stream->conn = conn;
- rv = nghttp3_map_insert(&conn->streams, &stream->me);
+ rv = nghttp3_map_insert(&conn->streams,
+ (nghttp3_map_key_type)stream->node.nid.id, stream);
if (rv != 0) {
nghttp3_stream_del(stream);
return rv;
@@ -2500,53 +1816,9 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
return 0;
}
-int nghttp3_conn_create_push_promise(nghttp3_conn *conn,
- nghttp3_push_promise **ppp,
- int64_t push_id,
- nghttp3_tnode *assoc_tnode) {
- nghttp3_push_promise *pp;
- int rv;
-
- rv = nghttp3_push_promise_new(&pp, push_id, conn->next_seq, assoc_tnode,
- conn->mem);
- if (rv != 0) {
- return rv;
- }
-
- rv = nghttp3_map_insert(&conn->pushes, &pp->me);
- if (rv != 0) {
- nghttp3_push_promise_del(pp, conn->mem);
- return rv;
- }
-
- ++conn->next_seq;
- *ppp = pp;
-
- return 0;
-}
-
nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn,
int64_t stream_id) {
- nghttp3_map_entry *me;
-
- me = nghttp3_map_find(&conn->streams, (key_type)stream_id);
- if (me == NULL) {
- return NULL;
- }
-
- return nghttp3_struct_of(me, nghttp3_stream, me);
-}
-
-nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn,
- int64_t push_id) {
- nghttp3_map_entry *me;
-
- me = nghttp3_map_find(&conn->pushes, (key_type)push_id);
- if (me == NULL) {
- return NULL;
- }
-
- return nghttp3_struct_of(me, nghttp3_push_promise, me);
+ return nghttp3_map_find(&conn->streams, (nghttp3_map_key_type)stream_id);
}
int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) {
@@ -2680,15 +1952,6 @@ nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
}
if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) {
- if (!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED) &&
- !(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
- conn->remote.uni.unsent_max_pushes > conn->remote.uni.max_pushes) {
- rv = nghttp3_conn_submit_max_push_id(conn);
- if (rv != 0) {
- return rv;
- }
- }
-
ncnt =
conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl);
if (ncnt) {
@@ -2727,7 +1990,7 @@ nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
return ncnt;
}
- if (nghttp3_stream_bidi_or_push(stream) &&
+ if (nghttp3_client_stream_bidi(stream->node.nid.id) &&
!nghttp3_stream_require_schedule(stream)) {
nghttp3_conn_unschedule_stream(conn, stream);
}
@@ -2748,10 +2011,6 @@ nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) {
tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe);
- if (tnode->nid.type == NGHTTP3_NODE_ID_TYPE_PUSH) {
- return nghttp3_struct_of(tnode, nghttp3_push_promise, node)->stream;
- }
-
return nghttp3_struct_of(tnode, nghttp3_stream, node);
}
@@ -2764,7 +2023,7 @@ int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id,
int rv;
if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ return 0;
}
rv = nghttp3_stream_add_outq_offset(stream, n);
@@ -2774,7 +2033,7 @@ int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id,
stream->unscheduled_nwrite += n;
- if (!nghttp3_stream_bidi_or_push(stream)) {
+ if (!nghttp3_client_stream_bidi(stream->node.nid.id)) {
return 0;
}
@@ -2795,7 +2054,7 @@ int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id,
nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ return 0;
}
return nghttp3_stream_add_ack_offset(stream, n);
@@ -2840,21 +2099,12 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream,
return 0;
}
-static nghttp3_tnode *stream_get_dependency_node(nghttp3_stream *stream) {
- if (stream->pp) {
- assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH);
- return &stream->pp->node;
- }
-
- return &stream->node;
-}
-
int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
/* Assume that stream stays on the same urgency level */
+ nghttp3_tnode *node = stream_get_sched_node(stream);
int rv;
- rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream),
- conn_get_sched_pq(conn, &stream->node),
+ rv = nghttp3_tnode_schedule(node, conn_get_sched_pq(conn, node),
stream->unscheduled_nwrite);
if (rv != 0) {
return rv;
@@ -2867,7 +2117,7 @@ int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
nghttp3_stream *stream) {
- if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) {
+ if (nghttp3_tnode_is_scheduled(stream_get_sched_node(stream))) {
return 0;
}
@@ -2876,8 +2126,9 @@ int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
void nghttp3_conn_unschedule_stream(nghttp3_conn *conn,
nghttp3_stream *stream) {
- nghttp3_tnode_unschedule(stream_get_dependency_node(stream),
- conn_get_sched_pq(conn, &stream->node));
+ nghttp3_tnode *node = stream_get_sched_node(stream);
+
+ nghttp3_tnode_unschedule(node, conn_get_sched_pq(conn, node));
}
int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id,
@@ -2983,184 +2234,6 @@ int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id,
return conn_submit_headers_data(conn, stream, nva, nvlen, NULL);
}
-int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, int64_t *ppush_id,
- int64_t stream_id, const nghttp3_nv *nva,
- size_t nvlen) {
- nghttp3_stream *stream;
- int rv;
- nghttp3_nv *nnva;
- nghttp3_frame_entry frent;
- int64_t push_id;
- nghttp3_push_promise *pp;
-
- assert(conn->server);
- assert(conn->tx.qenc);
- assert(nghttp3_client_stream_bidi(stream_id));
-
- if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) {
- return NGHTTP3_ERR_CONN_CLOSING;
- }
-
- stream = nghttp3_conn_find_stream(conn, stream_id);
- if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
- }
-
- if (conn->local.uni.max_pushes <= (uint64_t)conn->local.uni.next_push_id) {
- return NGHTTP3_ERR_PUSH_ID_BLOCKED;
- }
-
- push_id = conn->local.uni.next_push_id;
-
- rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node);
- if (rv != 0) {
- return rv;
- }
-
- ++conn->local.uni.next_push_id;
-
- rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem);
- if (rv != 0) {
- return rv;
- }
-
- frent.fr.hd.type = NGHTTP3_FRAME_PUSH_PROMISE;
- frent.fr.push_promise.push_id = push_id;
- frent.fr.push_promise.nva = nnva;
- frent.fr.push_promise.nvlen = nvlen;
-
- rv = nghttp3_stream_frq_add(stream, &frent);
- if (rv != 0) {
- nghttp3_nva_del(nnva, conn->mem);
- return rv;
- }
-
- *ppush_id = push_id;
-
- if (nghttp3_stream_require_schedule(stream)) {
- return nghttp3_conn_schedule_stream(conn, stream);
- }
-
- return 0;
-}
-
-int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, int64_t push_id,
- int64_t stream_id) {
- nghttp3_push_promise *pp;
- nghttp3_stream *stream;
- int rv;
-
- assert(conn->server);
- assert(nghttp3_server_stream_uni(stream_id));
-
- pp = nghttp3_conn_find_push_promise(conn, push_id);
- if (pp == NULL) {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
- }
-
- assert(NULL == nghttp3_conn_find_stream(conn, stream_id));
-
- rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
- if (rv != 0) {
- return rv;
- }
-
- stream->type = NGHTTP3_STREAM_TYPE_PUSH;
- stream->pp = pp;
-
- pp->stream = stream;
-
- rv = nghttp3_stream_write_stream_type_push_id(stream);
- if (rv != 0) {
- return rv;
- }
-
- return 0;
-}
-
-int nghttp3_conn_cancel_push(nghttp3_conn *conn, int64_t push_id) {
- if (conn->server) {
- return nghttp3_conn_server_cancel_push(conn, push_id);
- }
- return nghttp3_conn_client_cancel_push(conn, push_id);
-}
-
-int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id) {
- nghttp3_push_promise *pp;
- nghttp3_frame_entry frent;
- int rv;
-
- assert(conn->tx.ctrl);
-
- pp = nghttp3_conn_find_push_promise(conn, push_id);
- if (pp == NULL) {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
- }
-
- if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) {
- frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH;
- frent.fr.cancel_push.push_id = push_id;
-
- rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
- if (rv != 0) {
- return rv;
- }
-
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL;
- }
-
- if (pp->stream) {
- /* CANCEL_PUSH will be sent, but it does not affect pushed stream.
- Stream should be explicitly cancelled. */
- rv = conn_call_reset_stream(conn, pp->stream, NGHTTP3_H3_REQUEST_CANCELLED);
- if (rv != 0) {
- return rv;
- }
- }
-
- nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node));
-
- conn_delete_push_promise(conn, pp);
-
- return 0;
-}
-
-int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id) {
- nghttp3_push_promise *pp;
- nghttp3_frame_entry frent;
- int rv;
-
- pp = nghttp3_conn_find_push_promise(conn, push_id);
- if (pp == NULL) {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
- }
-
- if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL) {
- return 0;
- }
-
- if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED)) {
- return NGHTTP3_ERR_INVALID_STATE;
- }
-
- if (pp->stream) {
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL;
- return nghttp3_conn_cancel_push_stream(conn, pp->stream);
- }
-
- frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH;
- frent.fr.cancel_push.push_id = push_id;
-
- rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
- if (rv != 0) {
- return rv;
- }
-
- conn_delete_push_promise(conn, pp);
-
- return 0;
-}
-
int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) {
nghttp3_frame_entry frent;
int rv;
@@ -3168,7 +2241,8 @@ int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) {
assert(conn->tx.ctrl);
frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
- frent.fr.goaway.id = conn->server ? (1ull << 62) - 4 : (1ull << 62) - 1;
+ frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID
+ : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID;
assert(frent.fr.goaway.id <= conn->tx.goaway_id);
@@ -3194,7 +2268,7 @@ int nghttp3_conn_shutdown(nghttp3_conn *conn) {
frent.fr.goaway.id =
nghttp3_min((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4);
} else {
- frent.fr.goaway.id = nghttp3_min((1ll << 62) - 1, conn->rx.max_push_id + 1);
+ frent.fr.goaway.id = 0;
}
assert(frent.fr.goaway.id <= conn->tx.goaway_id);
@@ -3213,12 +2287,7 @@ int nghttp3_conn_shutdown(nghttp3_conn *conn) {
int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
int rv;
- rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id);
- if (rv != 0) {
- return rv;
- }
-
- rv = conn_call_send_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
if (rv != 0) {
return rv;
}
@@ -3226,74 +2295,46 @@ int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
return conn_call_reset_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
}
-static int conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream,
- uint64_t app_error_code) {
- int rv;
-
- /* TODO Send Stream Cancellation if we have not processed all
- incoming stream data up to fin */
- rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id);
- if (rv != 0) {
- return rv;
- }
-
- return conn_call_send_stop_sending(conn, stream, app_error_code);
-}
-
-int nghttp3_conn_reject_push_stream(nghttp3_conn *conn,
- nghttp3_stream *stream) {
- return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
-}
-
-int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn,
- nghttp3_stream *stream) {
- return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_CANCELLED);
-}
-
-int nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) {
+void nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ return;
}
stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED;
stream->unscheduled_nwrite = 0;
- if (nghttp3_stream_bidi_or_push(stream)) {
+ if (nghttp3_client_stream_bidi(stream->node.nid.id)) {
nghttp3_conn_unschedule_stream(conn, stream);
}
-
- return 0;
}
-int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) {
+void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ return;
}
stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR;
stream->unscheduled_nwrite = 0;
- if (nghttp3_stream_bidi_or_push(stream)) {
+ if (nghttp3_client_stream_bidi(stream->node.nid.id)) {
nghttp3_conn_unschedule_stream(conn, stream);
}
-
- return 0;
}
int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ return 0;
}
stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED;
- if (nghttp3_stream_bidi_or_push(stream) &&
+ if (nghttp3_client_stream_bidi(stream->node.nid.id) &&
nghttp3_stream_require_schedule(stream)) {
return nghttp3_conn_ensure_stream_scheduled(conn, stream);
}
@@ -3301,16 +2342,29 @@ int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) {
return 0;
}
+int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ return (stream->flags &
+ (NGHTTP3_STREAM_FLAG_FC_BLOCKED |
+ NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED | NGHTTP3_STREAM_FLAG_SHUT_WR |
+ NGHTTP3_STREAM_FLAG_CLOSED)) == 0;
+}
+
int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ return 0;
}
stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED;
- if (nghttp3_stream_bidi_or_push(stream) &&
+ if (nghttp3_client_stream_bidi(stream->node.nid.id) &&
nghttp3_stream_require_schedule(stream)) {
return nghttp3_conn_ensure_stream_scheduled(conn, stream);
}
@@ -3336,9 +2390,7 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id,
nghttp3_conn_unschedule_stream(conn, stream);
- if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX &&
- (conn->server || !stream->pp ||
- (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED))) {
+ if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX) {
return conn_delete_stream(conn, stream);
}
@@ -3346,13 +2398,22 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id,
return 0;
}
-int nghttp3_conn_reset_stream(nghttp3_conn *conn, int64_t stream_id) {
+int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_stream *stream;
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return 0;
+ }
+
stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream) {
- stream->flags |= NGHTTP3_STREAM_FLAG_RESET;
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) {
+ return 0;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_RD;
}
+
return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id);
}
@@ -3383,26 +2444,6 @@ void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn,
max_concurrent_streams);
}
-int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn) {
- nghttp3_frame_entry frent;
- int rv;
-
- assert(conn->tx.ctrl);
- assert(!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED));
-
- frent.fr.hd.type = NGHTTP3_FRAME_MAX_PUSH_ID;
- /* The acutal push_id is set when frame is serialized */
-
- rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
- if (rv != 0) {
- return rv;
- }
-
- conn->flags |= NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED;
-
- return 0;
-}
-
int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id,
void *stream_user_data) {
nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
@@ -3416,48 +2457,66 @@ int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id,
return 0;
}
-int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
- int64_t stream_id) {
+uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
+ int64_t stream_id) {
nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
- return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ return 0;
}
- return stream->rstate.left;
+ return (uint64_t)stream->rstate.left;
}
int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, nghttp3_pri *dest,
int64_t stream_id) {
- nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+ nghttp3_stream *stream;
assert(conn->server);
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
return NGHTTP3_ERR_STREAM_NOT_FOUND;
}
- dest->urgency = nghttp3_pri_uint8_urgency(stream->rx.http.pri);
- dest->inc = nghttp3_pri_uint8_inc(stream->rx.http.pri);
+ dest->urgency = nghttp3_pri_uint8_urgency(stream->node.pri);
+ dest->inc = nghttp3_pri_uint8_inc(stream->node.pri);
return 0;
}
int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, int64_t stream_id,
const nghttp3_pri *pri) {
- nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+ nghttp3_stream *stream;
+ nghttp3_frame_entry frent;
- assert(conn->server);
assert(pri->urgency < NGHTTP3_URGENCY_LEVELS);
assert(pri->inc == 0 || pri->inc == 1);
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
return NGHTTP3_ERR_STREAM_NOT_FOUND;
}
- stream->rx.http.pri = nghttp3_pri_to_uint8(pri);
+ if (conn->server) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET;
- return conn_update_stream_priority(conn, stream, stream->rx.http.pri);
+ return conn_update_stream_priority(conn, stream, nghttp3_pri_to_uint8(pri));
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
+ frent.fr.priority_update.pri_elem_id = stream_id;
+ frent.fr.priority_update.pri = *pri;
+
+ return nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
}
int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
@@ -3472,52 +2531,12 @@ int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
}
-void nghttp3_settings_default(nghttp3_settings *settings) {
+void nghttp3_settings_default_versioned(int settings_version,
+ nghttp3_settings *settings) {
+ (void)settings_version;
+
memset(settings, 0, sizeof(nghttp3_settings));
settings->max_field_section_size = NGHTTP3_VARINT_MAX;
-}
-
-int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id,
- uint64_t seq, nghttp3_tnode *assoc_tnode,
- const nghttp3_mem *mem) {
- nghttp3_push_promise *pp;
- nghttp3_node_id nid;
-
- pp = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_push_promise));
- if (pp == NULL) {
- return NGHTTP3_ERR_NOMEM;
- }
-
- nghttp3_tnode_init(
- &pp->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PUSH, push_id),
- seq, NGHTTP3_DEFAULT_URGENCY);
-
- pp->me.key = (key_type)push_id;
- pp->node.nid.id = push_id;
- pp->http.status_code = -1;
- pp->http.content_length = -1;
-
- if (assoc_tnode) {
- assert(assoc_tnode->nid.type == NGHTTP3_NODE_ID_TYPE_STREAM);
-
- pp->stream_id = assoc_tnode->nid.id;
- pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND;
- } else {
- pp->stream_id = -1;
- }
-
- *ppp = pp;
-
- return 0;
-}
-
-void nghttp3_push_promise_del(nghttp3_push_promise *pp,
- const nghttp3_mem *mem) {
- if (pp == NULL) {
- return;
- }
-
- nghttp3_tnode_free(&pp->node);
-
- nghttp3_mem_free(mem, pp);
+ settings->qpack_encoder_max_dtable_capacity =
+ NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY;
}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h
index f0f012e1770..fa7071e4b1d 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h
@@ -48,80 +48,38 @@
blocked streams for QPACK encoder. */
#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100
-/* NGHTTP3_PUSH_PROMISE_FLAG_NONE indicates that no flag is set. */
-#define NGHTTP3_PUSH_PROMISE_FLAG_NONE 0x00
-/* NGHTTP3_PUSH_PROMISE_FLAG_RECVED is set when PUSH_PROMISE is
- completely received. */
-#define NGHTTP3_PUSH_PROMISE_FLAG_RECVED 0x01
-/* NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL is set when push is
- cancelled by server before receiving PUSH_PROMISE completely.
- This flag should have no effect if push stream has already
- opened. */
-#define NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL 0x02
-/* NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL is set when push is
- canceled by the local endpoint. */
-#define NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL 0x04
-#define NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED \
- (NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL | \
- NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)
-/* NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED indicates that
- unsent_max_pushes has been updated for this push ID. */
-#define NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED 0x08
-/* NGHTTP3_PUSH_PROMISE_FLAG_BOUND is set if nghttp3_push_promise
- object is bound to a stream where PUSH_PROMISE frame is received
- first. nghttp3_push_promise object might be created before
- receiving any PUSH_PROMISE when pushed stream is received before
- it.*/
-#define NGHTTP3_PUSH_PROMISE_FLAG_BOUND 0x10
-
-typedef struct nghttp3_push_promise {
- nghttp3_map_entry me;
- nghttp3_tnode node;
- nghttp3_http_state http;
- /* stream is server initiated unidirectional stream which fulfils
- the push promise. */
- nghttp3_stream *stream;
- /* stream_id is the stream ID where this PUSH_PROMISE is first
- received. PUSH_PROMISE with same push ID is allowed to be sent
- in the multiple streams. We ignore those duplicated PUSH_PROMISE
- entirely because we don't see any value to add extra cost of
- processing for it. Even ignoring those frame is not yet easy
- because we have to decode the header blocks. Server push is
- overly complex and there is no good use case after all. */
- int64_t stream_id;
- /* flags is bitwise OR of zero or more of
- NGHTTP3_PUSH_PROMISE_FLAG_*. */
- uint16_t flags;
-} nghttp3_push_promise;
-
/* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */
-#define NGHTTP3_CONN_FLAG_NONE 0x0000
+#define NGHTTP3_CONN_FLAG_NONE 0x0000u
/* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has
been received. */
-#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001
+#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001u
/* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has
opened. */
-#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002
+#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002u
/* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder
stream has opened. */
-#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004
+#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004u
/* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder
stream has opened. */
-#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008
-/* NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED indicates that MAX_PUSH_ID has
- been queued to control stream. */
-#define NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED 0x0010
+#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008u
/* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has
received. */
-#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020
+#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020u
/* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has
been submitted for transmission. */
-#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040
+#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040u
+
+typedef struct nghttp3_chunk {
+ nghttp3_opl_entry oplent;
+} nghttp3_chunk;
+
+nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent);
struct nghttp3_conn {
+ nghttp3_objalloc out_chunk_objalloc;
+ nghttp3_objalloc stream_objalloc;
nghttp3_callbacks callbacks;
nghttp3_map streams;
- nghttp3_map pushes;
nghttp3_qpack_decoder qdec;
nghttp3_qpack_encoder qenc;
nghttp3_pq qpack_blocked_streams;
@@ -138,11 +96,9 @@ struct nghttp3_conn {
nghttp3_settings settings;
struct {
/* max_pushes is the number of push IDs that local endpoint can
- issue. This field is used by server only. */
+ issue. This field is used by server only and used just for
+ validation */
uint64_t max_pushes;
- /* next_push_id is the next push ID server uses. This field is
- used by server only. */
- int64_t next_push_id;
} uni;
} local;
@@ -154,18 +110,6 @@ struct nghttp3_conn {
issue. This field is used on server side only. */
uint64_t max_client_streams;
} bidi;
- struct {
- /* push_idtr tracks which push ID has been used by remote
- server. This field is used by client only. */
- nghttp3_gaptr push_idtr;
- /* unsent_max_pushes is the maximum number of push which the local
- endpoint can accept. This limit is not yet notified to the
- remote endpoint. This field is used by client only. */
- uint64_t unsent_max_pushes;
- /* max_push is the maximum number of push which the local
- endpoint can accept. This field is used by client only. */
- uint64_t max_pushes;
- } uni;
nghttp3_settings settings;
} remote;
@@ -174,7 +118,13 @@ struct nghttp3_conn {
int64_t goaway_id;
int64_t max_stream_id_bidi;
- int64_t max_push_id;
+
+ /* pri_fieldbuf is a buffer to store incoming Priority Field Value
+ in PRIORITY_UPDATE frame. */
+ uint8_t pri_fieldbuf[8];
+ /* pri_fieldlen is the number of bytes written into
+ pri_fieldbuf. */
+ size_t pri_fieldbuflen;
} rx;
struct {
@@ -192,17 +142,9 @@ struct nghttp3_conn {
nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id);
-nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn,
- int64_t push_id);
-
int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
int64_t stream_id);
-int nghttp3_conn_create_push_promise(nghttp3_conn *conn,
- nghttp3_push_promise **ppp,
- int64_t push_id,
- nghttp3_tnode *assoc_tnode);
-
nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
nghttp3_stream *stream, const uint8_t *src,
size_t srclen, int fin);
@@ -214,10 +156,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
nghttp3_stream *stream,
const uint8_t *src, size_t srclen);
-nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc,
- nghttp3_stream *stream, const uint8_t *src,
- size_t srclen, int fin);
-
nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn,
const uint8_t *src,
size_t srclen);
@@ -226,24 +164,14 @@ nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
const uint8_t *src,
size_t srclen);
-int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id,
- nghttp3_stream *stream);
-
-int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn,
- const nghttp3_frame_cancel_push *fr);
-
-int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn,
- const nghttp3_frame_cancel_push *fr);
-
-int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream,
- int64_t push_id);
-
int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
const uint8_t *data, size_t datalen);
+int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr);
+
nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn,
nghttp3_stream *stream,
- nghttp3_push_promise *pp,
const uint8_t *data, size_t datalen,
int fin);
@@ -255,12 +183,6 @@ int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn);
-int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id);
-
-int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id);
-
-int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn);
-
int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
@@ -270,20 +192,10 @@ void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream);
-int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream);
-
-int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, nghttp3_stream *stream);
-
/*
* nghttp3_conn_get_next_tx_stream returns next stream to send. It
* returns NULL if there is no such stream.
*/
nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn);
-int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id,
- uint64_t seq, nghttp3_tnode *assoc_tnode,
- const nghttp3_mem *mem);
-
-void nghttp3_push_promise_del(nghttp3_push_promise *pp, const nghttp3_mem *mem);
-
#endif /* NGHTTP3_CONN_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c
index 04bf6a0f298..cb340ab5a11 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c
@@ -58,6 +58,7 @@ int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) {
}
assert(0);
+ abort();
}
int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; }
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h
index 860326385ba..23555be7cac 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h
@@ -88,7 +88,7 @@
STIN uint32_t htonl(uint32_t hostlong) {
uint32_t res;
unsigned char *p = (unsigned char *)&res;
- *p++ = hostlong >> 24;
+ *p++ = (unsigned char)(hostlong >> 24);
*p++ = (hostlong >> 16) & 0xffu;
*p++ = (hostlong >> 8) & 0xffu;
*p = hostlong & 0xffu;
@@ -98,7 +98,7 @@ STIN uint32_t htonl(uint32_t hostlong) {
STIN uint16_t htons(uint16_t hostshort) {
uint16_t res;
unsigned char *p = (unsigned char *)&res;
- *p++ = hostshort >> 8;
+ *p++ = (unsigned char)(hostshort >> 8);
*p = hostshort & 0xffu;
return res;
}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c
index fb1353ea8a4..5cf94db852f 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c
@@ -36,8 +36,6 @@ const char *nghttp3_strerror(int liberr) {
return "ERR_WOULDBLOCK";
case NGHTTP3_ERR_STREAM_IN_USE:
return "ERR_STREAM_IN_USE";
- case NGHTTP3_ERR_PUSH_ID_BLOCKED:
- return "ERR_PUSH_ID_BLOCKED";
case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
return "ERR_MALFORMED_HTTP_HEADER";
case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
@@ -48,14 +46,12 @@ const char *nghttp3_strerror(int liberr) {
return "ERR_QPACK_FATAL";
case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE:
return "ERR_QPACK_HEADER_TOO_LARGE";
- case NGHTTP3_ERR_IGNORE_STREAM:
- return "ERR_IGNORE_STREAM";
case NGHTTP3_ERR_STREAM_NOT_FOUND:
return "ERR_STREAM_NOT_FOUND";
- case NGHTTP3_ERR_IGNORE_PUSH_PROMISE:
- return "ERR_IGNORE_PUSH_PROMISE";
case NGHTTP3_ERR_CONN_CLOSING:
return "ERR_CONN_CLOSING";
+ case NGHTTP3_ERR_STREAM_DATA_OVERFLOW:
+ return "ERR_STREAM_DATA_OVERFLOW";
case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED:
return "ERR_QPACK_DECOMPRESSION_FAILED";
case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
@@ -106,6 +102,8 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) {
case NGHTTP3_ERR_H3_MISSING_SETTINGS:
return NGHTTP3_H3_MISSING_SETTINGS;
case NGHTTP3_ERR_H3_INTERNAL_ERROR:
+ case NGHTTP3_ERR_NOMEM:
+ case NGHTTP3_ERR_CALLBACK_FAILURE:
return NGHTTP3_H3_INTERNAL_ERROR;
case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
return NGHTTP3_H3_CLOSED_CRITICAL_STREAM;
@@ -124,3 +122,5 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) {
return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
}
}
+
+int nghttp3_err_is_fatal(int liberr) { return liberr < NGHTTP3_ERR_FATAL; }
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c
index 6be82c6edaf..38c395ebe16 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c
@@ -26,6 +26,7 @@
#include "nghttp3_frame.h"
#include <string.h>
+#include <assert.h>
#include "nghttp3_conv.h"
#include "nghttp3_str.h"
@@ -70,59 +71,53 @@ size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen,
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
}
-uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p,
- const nghttp3_frame_cancel_push *fr) {
+uint8_t *nghttp3_frame_write_goaway(uint8_t *p,
+ const nghttp3_frame_goaway *fr) {
p = nghttp3_frame_write_hd(p, &fr->hd);
- p = nghttp3_put_varint(p, fr->push_id);
+ p = nghttp3_put_varint(p, fr->id);
return p;
}
-size_t
-nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen,
- const nghttp3_frame_cancel_push *fr) {
- size_t payloadlen = nghttp3_put_varint_len(fr->push_id);
+size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
+ const nghttp3_frame_goaway *fr) {
+ size_t payloadlen = nghttp3_put_varint_len(fr->id);
*ppayloadlen = (int64_t)payloadlen;
- return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) +
+ return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) +
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
}
-uint8_t *nghttp3_frame_write_max_push_id(uint8_t *p,
- const nghttp3_frame_max_push_id *fr) {
+uint8_t *
+nghttp3_frame_write_priority_update(uint8_t *p,
+ const nghttp3_frame_priority_update *fr) {
p = nghttp3_frame_write_hd(p, &fr->hd);
- p = nghttp3_put_varint(p, fr->push_id);
+ p = nghttp3_put_varint(p, fr->pri_elem_id);
- return p;
-}
-
-size_t
-nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen,
- const nghttp3_frame_max_push_id *fr) {
- size_t payloadlen = nghttp3_put_varint_len(fr->push_id);
+ assert(fr->pri.urgency <= NGHTTP3_URGENCY_LOW);
- *ppayloadlen = (int64_t)payloadlen;
+ *p++ = 'u';
+ *p++ = '=';
+ *p++ = (uint8_t)('0' + fr->pri.urgency);
- return nghttp3_put_varint_len(NGHTTP3_FRAME_MAX_PUSH_ID) +
- nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
-}
-
-uint8_t *nghttp3_frame_write_goaway(uint8_t *p,
- const nghttp3_frame_goaway *fr) {
- p = nghttp3_frame_write_hd(p, &fr->hd);
- p = nghttp3_put_varint(p, fr->id);
+ if (fr->pri.inc) {
+#define NGHTTP3_PRIORITY_INCREMENTAL ", i"
+ p = nghttp3_cpymem(p, (const uint8_t *)NGHTTP3_PRIORITY_INCREMENTAL,
+ sizeof(NGHTTP3_PRIORITY_INCREMENTAL) - 1);
+ }
return p;
}
-size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
- const nghttp3_frame_goaway *fr) {
- size_t payloadlen = nghttp3_put_varint_len(fr->id);
+size_t nghttp3_frame_write_priority_update_len(
+ int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr) {
+ size_t payloadlen = nghttp3_put_varint_len(fr->pri_elem_id) + sizeof("u=U") -
+ 1 + (fr->pri.inc ? sizeof(", i") - 1 : 0);
*ppayloadlen = (int64_t)payloadlen;
- return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) +
+ return nghttp3_put_varint_len(fr->hd.type) +
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
}
@@ -207,12 +202,3 @@ void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
nghttp3_nva_del(fr->nva, mem);
}
-
-void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr,
- const nghttp3_mem *mem) {
- if (fr == NULL) {
- return;
- }
-
- nghttp3_nva_del(fr->nva, mem);
-}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h
index 9bd59a9660d..b64bbc4ecb9 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h
@@ -42,6 +42,10 @@ typedef enum nghttp3_frame_type {
NGHTTP3_FRAME_PUSH_PROMISE = 0x05,
NGHTTP3_FRAME_GOAWAY = 0x07,
NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d,
+ /* PRIORITY_UPDATE:
+ https://tools.ietf.org/html/draft-ietf-httpbis-priority-03 */
+ NGHTTP3_FRAME_PRIORITY_UPDATE = 0x0f0700,
+ NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID = 0x0f0701,
} nghttp3_frame_type;
typedef enum nghttp3_h2_reserved_type {
@@ -66,14 +70,10 @@ typedef struct nghttp3_frame_headers {
size_t nvlen;
} nghttp3_frame_headers;
-typedef struct nghttp3_frame_cancel_push {
- nghttp3_frame_hd hd;
- int64_t push_id;
-} nghttp3_frame_cancel_push;
-
#define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06
#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01
#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07
+#define NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL 0x08
#define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2
#define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3
@@ -91,32 +91,28 @@ typedef struct nghttp3_frame_settings {
nghttp3_settings_entry iv[1];
} nghttp3_frame_settings;
-typedef struct nghttp3_frame_push_promise {
- nghttp3_frame_hd hd;
- nghttp3_nv *nva;
- size_t nvlen;
- int64_t push_id;
-} nghttp3_frame_push_promise;
-
typedef struct nghttp3_frame_goaway {
nghttp3_frame_hd hd;
int64_t id;
} nghttp3_frame_goaway;
-typedef struct nghttp3_frame_max_push_id {
+typedef struct nghttp3_frame_priority_update {
nghttp3_frame_hd hd;
- int64_t push_id;
-} nghttp3_frame_max_push_id;
+ /* pri_elem_id is stream ID if hd.type ==
+ NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type ==
+ NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined
+ otherwise. */
+ int64_t pri_elem_id;
+ nghttp3_pri pri;
+} nghttp3_frame_priority_update;
typedef union nghttp3_frame {
nghttp3_frame_hd hd;
nghttp3_frame_data data;
nghttp3_frame_headers headers;
- nghttp3_frame_cancel_push cancel_push;
nghttp3_frame_settings settings;
- nghttp3_frame_push_promise push_promise;
nghttp3_frame_goaway goaway;
- nghttp3_frame_max_push_id max_push_id;
+ nghttp3_frame_priority_update priority_update;
} nghttp3_frame;
/*
@@ -151,42 +147,6 @@ size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen,
const nghttp3_frame_settings *fr);
/*
- * nghttp3_frame_write_cancel_push writes CANCEL_PUSH frame |fr| to
- * |dest|. This function assumes that |dest| has enough space to
- * write |fr|.
- *
- * This function returns |dest| plus the number of bytes written.
- */
-uint8_t *nghttp3_frame_write_cancel_push(uint8_t *dest,
- const nghttp3_frame_cancel_push *fr);
-
-/*
- * nghttp3_frame_write_cancel_push_len returns the number of bytes
- * required to write |fr|. fr->hd.length is ignored. This function
- * stores payload length in |*ppayloadlen|.
- */
-size_t nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen,
- const nghttp3_frame_cancel_push *fr);
-
-/*
- * nghttp3_frame_write_max_push_id writes MAX_PUSH_ID frame |fr| to
- * |dest|. This function assumes that |dest| has enough space to
- * write |fr|.
- *
- * This function returns |dest| plus the number of bytes written.
- */
-uint8_t *nghttp3_frame_write_max_push_id(uint8_t *dest,
- const nghttp3_frame_max_push_id *fr);
-
-/*
- * nghttp3_frame_write_max_push_id_len returns the number of bytes
- * required to write |fr|. fr->hd.length is ignored. This function
- * stores payload length in |*ppayloadlen|.
- */
-size_t nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen,
- const nghttp3_frame_max_push_id *fr);
-
-/*
* nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|.
* This function assumes that |dest| has enough space to write |fr|.
*
@@ -204,6 +164,25 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
const nghttp3_frame_goaway *fr);
/*
+ * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame
+ * |fr| to |dest|. This function assumes that |dest| has enough space
+ * to write |fr|.
+ *
+ * This function returns |dest| plus the number of bytes written;
+ */
+uint8_t *
+nghttp3_frame_write_priority_update(uint8_t *dest,
+ const nghttp3_frame_priority_update *fr);
+
+/*
+ * nghttp3_frame_write_priority_update_len returns the number of bytes
+ * required to write |fr|. fr->hd.length is ignored. This function
+ * stores payload length in |*ppayloadlen|.
+ */
+size_t nghttp3_frame_write_priority_update_len(
+ int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr);
+
+/*
* nghttp3_nva_copy copies name/value pairs from |nva|, which contains
* |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so
* that all items can be stored. The resultant name and value in
@@ -233,11 +212,4 @@ void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem);
void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
const nghttp3_mem *mem);
-/*
- * nghttp3_frame_push_promise_free frees memory allocated for |fr|.
- * It assumes that fr->nva is created by nghttp3_nva_copy() or NULL.
- */
-void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr,
- const nghttp3_mem *mem);
-
#endif /* NGHTTP3_FRAME_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c
index c60887d1ec4..88cb49a02f8 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c
@@ -24,29 +24,26 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp3_gaptr.h"
-#include "nghttp3_range.h"
#include <string.h>
#include <assert.h>
-int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) {
- int rv;
- nghttp3_range range = {0, UINT64_MAX};
+void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) {
+ nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range),
+ mem);
- rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar,
- sizeof(nghttp3_range), mem);
- if (rv != 0) {
- return rv;
- }
+ gaptr->mem = mem;
+}
+
+static int gaptr_gap_init(nghttp3_gaptr *gaptr) {
+ nghttp3_range range = {0, UINT64_MAX};
+ int rv;
rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL);
if (rv != 0) {
- nghttp3_ksl_free(&gaptr->gap);
return rv;
}
- gaptr->mem = mem;
-
return 0;
}
@@ -58,11 +55,19 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) {
nghttp3_ksl_free(&gaptr->gap);
}
-int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) {
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen) {
int rv;
nghttp3_range k, m, l, r, q = {offset, offset + datalen};
nghttp3_ksl_it it;
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ rv = gaptr_gap_init(gaptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
nghttp3_ksl_range_exclusive_compar);
@@ -74,7 +79,7 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) {
}
if (nghttp3_range_eq(&k, &m)) {
- nghttp3_ksl_remove(&gaptr->gap, &it, &k);
+ nghttp3_ksl_remove_hint(&gaptr->gap, &it, &it, &k);
continue;
}
nghttp3_range_cut(&l, &r, &k, &m);
@@ -96,35 +101,69 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) {
}
uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) {
- nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap);
- nghttp3_range r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+ nghttp3_ksl_it it;
+ nghttp3_range r;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = nghttp3_ksl_begin(&gaptr->gap);
+ r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+
return r.begin;
}
-nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
- uint64_t offset) {
+nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
+ uint64_t offset) {
nghttp3_range q = {offset, offset + 1};
- return nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
- nghttp3_ksl_range_exclusive_compar);
+ nghttp3_ksl_it it;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ nghttp3_range r = {0, UINT64_MAX};
+ return r;
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+
+ assert(!nghttp3_ksl_it_end(&it));
+
+ return *(nghttp3_range *)nghttp3_ksl_it_key(&it);
}
int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
- size_t datalen) {
+ uint64_t datalen) {
nghttp3_range q = {offset, offset + datalen};
- nghttp3_ksl_it it = nghttp3_ksl_lower_bound_compar(
- &gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar);
- nghttp3_range k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
- nghttp3_range m = nghttp3_range_intersect(&q, &k);
+ nghttp3_ksl_it it;
+ nghttp3_range k;
+ nghttp3_range m;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+ k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+ m = nghttp3_range_intersect(&q, &k);
+
return nghttp3_range_len(&m) == 0;
}
void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) {
- nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap);
+ nghttp3_ksl_it it;
nghttp3_range r;
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return;
+ }
+
+ it = nghttp3_ksl_begin(&gaptr->gap);
+
assert(!nghttp3_ksl_it_end(&it));
r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
- nghttp3_ksl_remove(&gaptr->gap, NULL, &r);
+ nghttp3_ksl_remove_hint(&gaptr->gap, NULL, &it, &r);
}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h
index b1713a048bb..7c83c847c9f 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h
@@ -34,6 +34,7 @@
#include "nghttp3_mem.h"
#include "nghttp3_ksl.h"
+#include "nghttp3_range.h"
/*
* nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX).
@@ -48,14 +49,8 @@ typedef struct nghttp3_gaptr {
/*
* nghttp3_gaptr_init initializes |gaptr|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGHTTP3_ERR_NOMEM
- * Out of memory.
*/
-int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem);
+void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem);
/*
* nghttp3_gaptr_free frees resources allocated for |gaptr|.
@@ -72,7 +67,7 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr);
* NGHTTP3_ERR_NOMEM
* Out of memory
*/
-int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen);
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen);
/*
* nghttp3_gaptr_first_gap_offset returns the offset to the first gap.
@@ -81,18 +76,18 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen);
uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr);
/*
- * nghttp3_gaptr_get_first_gap_after returns the iterator pointing to
- * the first gap which overlaps or comes after |offset|.
+ * nghttp3_gaptr_get_first_gap_after returns the first gap which
+ * overlaps or comes after |offset|.
*/
-nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
- uint64_t offset);
+nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
+ uint64_t offset);
/*
* nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset +
* datalen) is completely pushed into this object.
*/
int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
- size_t datalen);
+ uint64_t datalen);
/*
* nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c
index 465c36ab2cb..5e06d8c4765 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c
@@ -36,12 +36,17 @@ static uint8_t downcase(uint8_t c) {
return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
}
+/*
+ * memieq returns 1 if the data pointed by |a| of length |n| equals to
+ * |b| of the same length in case-insensitive manner. The data
+ * pointed by |a| must not include upper cased letters (A-Z).
+ */
static int memieq(const void *a, const void *b, size_t n) {
size_t i;
const uint8_t *aa = a, *bb = b;
for (i = 0; i < n; ++i) {
- if (downcase(aa[i]) != downcase(bb[i])) {
+ if (aa[i] != downcase(bb[i])) {
return 0;
}
}
@@ -73,25 +78,12 @@ static int64_t parse_uint(const uint8_t *s, size_t len) {
return n;
}
-static int lws(const uint8_t *s, size_t n) {
- size_t i;
- for (i = 0; i < n; ++i) {
- if (s[i] != ' ' && s[i] != '\t') {
- return 0;
- }
- }
- return 1;
-}
-
static int check_pseudo_header(nghttp3_http_state *http,
- const nghttp3_qpack_nv *nv, int flag) {
- if (http->flags & flag) {
- return 0;
- }
- if (lws(nv->value->base, nv->value->len)) {
+ const nghttp3_qpack_nv *nv, uint32_t flag) {
+ if ((http->flags & flag) || nv->value->len == 0) {
return 0;
}
- http->flags = (uint16_t)(http->flags | flag);
+ http->flags |= flag;
return 1;
}
@@ -106,98 +98,730 @@ static int expect_response_body(nghttp3_http_state *http) {
:path header field value must start with "/". This function must
be called after ":method" header field was received. This function
returns nonzero if path is valid.*/
-static int check_path(nghttp3_http_state *http) {
+static int check_path_flags(nghttp3_http_state *http) {
return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 ||
((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) ||
((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) &&
(http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK)));
}
-int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
- size_t len) {
- nghttp3_pri pri = *dest;
- const uint8_t *p = value, *end = value + len;
+/* Generated by genchartbl.py */
+static const int SF_KEY_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
+ 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */,
+ 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
+ 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */,
+ 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */,
+ 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
+ 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */,
+ 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */,
+ 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
+ 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
+ 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
- for (;;) {
- for (; p != end && (*p == ' ' || *p == '\t'); ++p)
- ;
+static nghttp3_ssize sf_parse_key(const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
- if (p == end) {
- break;
+ if ((*p < 'a' || 'z' < *p) && *p != '*') {
+ return -1;
+ }
+
+ for (; p != end && SF_KEY_CHARS[*p]; ++p)
+ ;
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_integer_or_decimal(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ int sign = 1;
+ int64_t value = 0;
+ int type = NGHTTP3_SF_VALUE_TYPE_INTEGER;
+ size_t len = 0;
+ size_t fpos = 0;
+ size_t i;
+
+ if (*p == '-') {
+ if (++p == end) {
+ return -1;
}
+ sign = -1;
+ }
+
+ if (*p < '0' || '9' < *p) {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
switch (*p) {
- case 'u':
- ++p;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ value *= 10;
+ value += *p - '0';
+
+ if (++len > 15) {
+ return -1;
+ }
- if (p + 2 > end || *p++ != '=') {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
+ break;
+ case '.':
+ if (type != NGHTTP3_SF_VALUE_TYPE_INTEGER) {
+ goto fin;
}
- if (!('0' <= *p && *p <= '7')) {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
+ if (len > 12) {
+ return -1;
}
+ fpos = len;
+ type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
- pri.urgency = (uint32_t)(*p++ - '0');
+ break;
+ default:
+ goto fin;
+ };
+ }
- if (p == end) {
- goto fin;
+fin:
+ switch (type) {
+ case NGHTTP3_SF_VALUE_TYPE_INTEGER:
+ if (dest) {
+ dest->type = (uint8_t)type;
+ dest->i = value * sign;
+ }
+
+ return p - begin;
+ case NGHTTP3_SF_VALUE_TYPE_DECIMAL:
+ if (fpos == len || len - fpos > 3) {
+ return -1;
+ }
+
+ if (dest) {
+ dest->type = (uint8_t)type;
+ dest->d = (double)value;
+ for (i = len - fpos; i > 0; --i) {
+ dest->d /= (double)10;
+ }
+ dest->d *= sign;
+ }
+
+ return p - begin;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+/* Generated by genchartbl.py */
+static const int SF_DQUOTE_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */,
+ 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */,
+ 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */,
+ 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */,
+ 1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_string(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if (*p++ != '"') {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case '\\':
+ if (++p == end) {
+ return -1;
}
- if (*p++ != ',') {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
+ switch (*p) {
+ case '"':
+ case '\\':
+ break;
+ default:
+ return -1;
}
break;
- case 'i':
+ case '"':
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_STRING;
+ dest->s.base = begin + 1;
+ dest->s.len = (size_t)(p - dest->s.base);
+ }
+
++p;
- if (p == end) {
- pri.inc = 1;
- goto fin;
+ return p - begin;
+ default:
+ if (!SF_DQUOTE_CHARS[*p]) {
+ return -1;
}
+ }
+ }
- if (*p == ',') {
- pri.inc = 1;
- ++p;
- break;
+ return -1;
+}
+
+/* Generated by genchartbl.py */
+static const int SF_TOKEN_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */,
+ 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */,
+ 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */,
+ 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */,
+ 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_token(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') {
+ return -1;
+ }
+
+ for (; p != end && SF_TOKEN_CHARS[*p]; ++p)
+ ;
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_TOKEN;
+ dest->s.base = begin;
+ dest->s.len = (size_t)(p - begin);
+ }
+
+ return p - begin;
+}
+
+/* Generated by genchartbl.py */
+static const int SF_BYTESEQ_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
+ 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
+ 0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */,
+ 0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
+ 0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
+ 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_byteseq(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if (*p++ != ':') {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case ':':
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_BYTESEQ;
+ dest->s.base = begin + 1;
+ dest->s.len = (size_t)(p - dest->s.base);
}
- if (p + 3 > end || *p != '=' || *(p + 1) != '?' ||
- (*(p + 2) != '0' && *(p + 2) != '1')) {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
+ ++p;
+
+ return p - begin;
+ default:
+ if (!SF_BYTESEQ_CHARS[*p]) {
+ return -1;
}
+ }
+ }
- pri.inc = *(p + 2) == '1';
+ return -1;
+}
- p += 3;
+static nghttp3_ssize sf_parse_boolean(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ int b;
- if (p == end) {
- goto fin;
+ if (*p++ != '?') {
+ return -1;
+ }
+
+ if (p == end) {
+ return -1;
+ }
+
+ switch (*p++) {
+ case '0':
+ b = 0;
+ break;
+ case '1':
+ b = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN;
+ dest->b = b;
+ }
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_bare_item(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ switch (*begin) {
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return sf_parse_integer_or_decimal(dest, begin, end);
+ case '"':
+ return sf_parse_string(dest, begin, end);
+ case '*':
+ return sf_parse_token(dest, begin, end);
+ case ':':
+ return sf_parse_byteseq(dest, begin, end);
+ case '?':
+ return sf_parse_boolean(dest, begin, end);
+ default:
+ if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) {
+ return sf_parse_token(dest, begin, end);
+ }
+ return -1;
+ }
+}
+
+#define sf_discard_sp_end_err(BEGIN, END, ERR) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ return (ERR); \
+ } \
+ if (*(BEGIN) != ' ') { \
+ break; \
+ } \
+ }
+
+static nghttp3_ssize sf_parse_params(const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ for (; p != end && *p == ';';) {
+ ++p;
+
+ sf_discard_sp_end_err(p, end, -1);
+
+ slen = sf_parse_key(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (p == end || *p != '=') {
+ /* Boolean true */
+ } else if (++p == end) {
+ return -1;
+ } else {
+ slen = sf_parse_bare_item(NULL, p, end);
+ if (slen < 0) {
+ return -1;
}
- if (*p++ != ',') {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
+ p += slen;
+ }
+ }
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_item(nghttp3_sf_value *dest, const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ slen = sf_parse_bare_item(dest, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ return p - begin;
+}
+
+nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ return sf_parse_item(dest, begin, end);
+}
+
+static nghttp3_ssize sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ if (*p++ != '(') {
+ return -1;
+ }
+
+ for (;;) {
+ sf_discard_sp_end_err(p, end, -1);
+
+ if (*p == ')') {
+ ++p;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return -1;
}
- break;
- default:
+ p += slen;
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_INNER_LIST;
+ }
+
+ return p - begin;
+ }
+
+ slen = sf_parse_item(NULL, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (p == end || (*p != ' ' && *p != ')')) {
+ return -1;
+ }
+ }
+}
+
+nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ return sf_parse_inner_list(dest, begin, end);
+}
+
+static nghttp3_ssize sf_parse_item_or_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ if (*begin == '(') {
+ return sf_parse_inner_list(dest, begin, end);
+ }
+
+ return sf_parse_item(dest, begin, end);
+}
+
+#define sf_discard_ows(BEGIN, END) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ goto fin; \
+ } \
+ if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
+ break; \
+ } \
+ }
+
+#define sf_discard_ows_end_err(BEGIN, END, ERR) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ return (ERR); \
+ } \
+ if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
+ break; \
+ } \
+ }
+
+int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
+ size_t valuelen) {
+ const uint8_t *p = value, *end = value + valuelen;
+ nghttp3_ssize slen;
+ nghttp3_sf_value val;
+ nghttp3_pri pri = *dest;
+ const uint8_t *key;
+ size_t keylen;
+
+ for (; p != end && *p == ' '; ++p)
+ ;
+
+ for (; p != end;) {
+ slen = sf_parse_key(p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ key = p;
+ keylen = (size_t)slen;
+
+ p += slen;
+
+ if (p == end || *p != '=') {
+ /* Boolean true */
+ val.type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN;
+ val.b = 1;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ } else if (++p == end) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
+ } else {
+ slen = sf_parse_item_or_inner_list(&val, p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ }
+
+ p += slen;
+
+ if (keylen == 1) {
+ switch (key[0]) {
+ case 'i':
+ if (val.type != NGHTTP3_SF_VALUE_TYPE_BOOLEAN) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.inc = val.b;
+
+ break;
+ case 'u':
+ if (val.type != NGHTTP3_SF_VALUE_TYPE_INTEGER ||
+ val.i < NGHTTP3_URGENCY_HIGH || NGHTTP3_URGENCY_LOW < val.i) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.urgency = (uint32_t)val.i;
+
+ break;
+ }
}
- if (p == end) {
+ sf_discard_ows(p, end);
+
+ if (*p++ != ',') {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
+
+ sf_discard_ows_end_err(p, end, NGHTTP3_ERR_INVALID_ARGUMENT);
}
fin:
-
*dest = pri;
return 0;
}
-static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type,
+static int http_request_on_header(nghttp3_http_state *http,
nghttp3_qpack_nv *nv, int trailers,
int connect_protocol) {
nghttp3_pri pri;
@@ -229,10 +853,6 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type,
switch (nv->value->base[6]) {
case 'T':
if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
- if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) {
- /* we won't allow CONNECT for push */
- return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
- }
http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
}
break;
@@ -259,8 +879,10 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type,
if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) {
return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
}
- if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
- (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
+ /* scheme is case-insensitive:
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 */
+ if (lstrieq("http", nv->value->base, nv->value->len) ||
+ lstrieq("https", nv->value->base, nv->value->len)) {
http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP;
}
break;
@@ -308,11 +930,17 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type,
}
break;
case NGHTTP3_QPACK_TOKEN_PRIORITY:
- pri.urgency = nghttp3_pri_uint8_urgency(http->pri);
- pri.inc = nghttp3_pri_uint8_inc(http->pri);
- if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) ==
- 0) {
- http->pri = nghttp3_pri_to_uint8(&pri);
+ if (!trailers && !(http->flags & NGHTTP3_HTTP_FLAG_BAD_PRIORITY)) {
+ pri.urgency = nghttp3_pri_uint8_urgency(http->pri);
+ pri.inc = nghttp3_pri_uint8_inc(http->pri);
+ if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) ==
+ 0) {
+ http->pri = nghttp3_pri_to_uint8(&pri);
+ http->flags |= NGHTTP3_HTTP_FLAG_PRIORITY;
+ } else {
+ http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY;
+ http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY;
+ }
}
break;
default:
@@ -509,8 +1137,167 @@ static int check_scheme(const uint8_t *value, size_t len) {
return 1;
}
-int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type,
- nghttp3_qpack_nv *nv, int request, int trailers) {
+/* Generated by genmethodchartbl.py */
+static char VALID_METHOD_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
+ 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
+ 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */,
+ 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
+ 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
+ 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
+ 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
+ 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
+ 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
+ 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
+ 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
+ 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
+ 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
+ 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
+ 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
+ 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
+ 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
+ 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
+ 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
+ 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
+ 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
+ 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
+ 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
+ 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
+ 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
+ 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
+ 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
+ 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
+ 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
+ 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
+};
+
+static int check_method(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_METHOD_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genpathchartbl.py */
+static char VALID_PATH_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
+ 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
+ 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
+ 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
+ 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
+ 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
+ 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
+ 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
+ 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
+ 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
+ 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
+ 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
+ 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
+ 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
+ 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
+ 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
+ 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
+ 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
+ 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
+ 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
+ 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
+ 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
+ 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
+ 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
+ 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
+ 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
+ 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
+ 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
+ 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
+ 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
+ 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
+ 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
+ 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
+ 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
+ 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
+};
+
+static int check_path(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_PATH_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv,
+ int request, int trailers, int connect_protocol) {
int rv;
size_t i;
uint8_t c;
@@ -535,12 +1322,27 @@ int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type,
assert(nv->name->len > 0);
- if (nv->token == NGHTTP3_QPACK_TOKEN__AUTHORITY ||
- nv->token == NGHTTP3_QPACK_TOKEN_HOST) {
- rv = check_authority(nv->value->base, nv->value->len);
- } else if (nv->token == NGHTTP3_QPACK_TOKEN__SCHEME) {
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__METHOD:
+ rv = check_method(nv->value->base, nv->value->len);
+ break;
+ case NGHTTP3_QPACK_TOKEN__SCHEME:
rv = check_scheme(nv->value->base, nv->value->len);
- } else {
+ break;
+ case NGHTTP3_QPACK_TOKEN__AUTHORITY:
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ if (request) {
+ rv = check_authority(nv->value->base, nv->value->len);
+ } else {
+ /* The use of host field in response field section is
+ undefined. */
+ rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ rv = check_path(nv->value->base, nv->value->len);
+ break;
+ default:
rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
}
@@ -556,8 +1358,7 @@ int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type,
}
if (request) {
- rv = http_request_on_header(http, frame_type, nv, trailers,
- /* connect_protocol = */ 0);
+ rv = http_request_on_header(http, nv, trailers, connect_protocol);
} else {
rv = http_response_on_header(http, nv, trailers);
}
@@ -594,7 +1395,7 @@ int nghttp3_http_on_request_headers(nghttp3_http_state *http) {
(http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) {
return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
}
- if (!check_path(http)) {
+ if (!check_path_flags(http)) {
return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
}
}
@@ -609,15 +1410,14 @@ int nghttp3_http_on_response_headers(nghttp3_http_state *http) {
if (http->status_code / 100 == 1) {
/* non-final response */
- http->flags = (uint16_t)((http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) |
- NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
+ http->flags = (http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) |
+ NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
http->content_length = -1;
http->status_code = -1;
return 0;
}
- http->flags =
- (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
+ http->flags &= ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
if (!expect_response_body(http)) {
http->content_length = 0;
@@ -629,9 +1429,6 @@ int nghttp3_http_on_response_headers(nghttp3_http_state *http) {
}
int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) {
- if (stream->flags & NGHTTP3_STREAM_FLAG_RESET) {
- return 0;
- }
if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
(stream->rx.http.content_length != -1 &&
stream->rx.http.content_length != stream->rx.http.recv_content_length)) {
@@ -835,6 +1632,19 @@ static const int VALID_HD_VALUE_CHARS[] = {
int nghttp3_check_header_value(const uint8_t *value, size_t len) {
const uint8_t *last;
+
+ switch (len) {
+ case 0:
+ return 1;
+ case 1:
+ return !(*value == ' ' || *value == '\t');
+ default:
+ if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' ||
+ *(value + len - 1) == '\t') {
+ return 0;
+ }
+ }
+
for (last = value + len; value != last; ++value) {
if (!VALID_HD_VALUE_CHARS[*value]) {
return 0;
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h
index 65ec91759ba..1617348ad14 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h
@@ -39,48 +39,55 @@ typedef struct nghttp3_http_state nghttp3_http_state;
/* HTTP related flags to enforce HTTP semantics */
/* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */
-#define NGHTTP3_HTTP_FLAG_NONE 0x00
+#define NGHTTP3_HTTP_FLAG_NONE 0x00u
/* header field seen so far */
-#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01
-#define NGHTTP3_HTTP_FLAG__PATH 0x02
-#define NGHTTP3_HTTP_FLAG__METHOD 0x04
-#define NGHTTP3_HTTP_FLAG__SCHEME 0x08
+#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01u
+#define NGHTTP3_HTTP_FLAG__PATH 0x02u
+#define NGHTTP3_HTTP_FLAG__METHOD 0x04u
+#define NGHTTP3_HTTP_FLAG__SCHEME 0x08u
/* host is not pseudo header, but we require either host or
:authority */
-#define NGHTTP3_HTTP_FLAG_HOST 0x10
-#define NGHTTP3_HTTP_FLAG__STATUS 0x20
+#define NGHTTP3_HTTP_FLAG_HOST 0x10u
+#define NGHTTP3_HTTP_FLAG__STATUS 0x20u
/* required header fields for HTTP request except for CONNECT
method. */
#define NGHTTP3_HTTP_FLAG_REQ_HEADERS \
(NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH | \
NGHTTP3_HTTP_FLAG__SCHEME)
-#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40
+#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40u
/* HTTP method flags */
-#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80
-#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100
-#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200
+#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80u
+#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100u
+#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200u
#define NGHTTP3_HTTP_FLAG_METH_ALL \
(NGHTTP3_HTTP_FLAG_METH_CONNECT | NGHTTP3_HTTP_FLAG_METH_HEAD | \
NGHTTP3_HTTP_FLAG_METH_OPTIONS)
/* :path category */
/* path starts with "/" */
-#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400
+#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400u
/* path "*" */
-#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800
+#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800u
/* scheme */
/* "http" or "https" scheme */
-#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000
+#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000u
/* set if final response is expected */
-#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000
+#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000u
/* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header
field is seen. */
-#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000
+#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000u
+/* NGHTTP3_HTTP_FLAG_PRIORITY is set when priority header field is
+ processed. */
+#define NGHTTP3_HTTP_FLAG_PRIORITY 0x8000u
+/* NGHTTP3_HTTP_FLAG_BAD_PRIORITY is set when an error is encountered
+ while parsing priority header field. */
+#define NGHTTP3_HTTP_FLAG_BAD_PRIORITY 0x010000u
/*
- * This function is called when HTTP header field |nv| in a frame of type
- * |frame_type| is received for |http|. This function will validate |nv|
- * against the current state of stream. Pass nonzero if this is request
- * headers. Pass nonzero to |trailers| if |nv| is included in trailers.
+ * This function is called when HTTP header field |nv| received for
+ * |http|. This function will validate |nv| against the current state
+ * of stream. Pass nonzero if this is request headers. Pass nonzero
+ * to |trailers| if |nv| is included in trailers. |connect_protocol|
+ * is nonzero if Extended CONNECT Method is enabled.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -91,8 +98,8 @@ typedef struct nghttp3_http_state nghttp3_http_state;
* Invalid HTTP header field was received but it can be treated as
* if it was not received because of compatibility reasons.
*/
-int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type,
- nghttp3_qpack_nv *nv, int request, int trailers);
+int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv,
+ int request, int trailers, int connect_protocol);
/*
* This function is called when request header is received. This
@@ -143,4 +150,53 @@ int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n);
void nghttp3_http_record_request_method(nghttp3_stream *stream,
const nghttp3_nv *nva, size_t nvlen);
+/*
+ * RFC 8941 Structured Field Values.
+ */
+typedef enum nghttp3_sf_value_type {
+ NGHTTP3_SF_VALUE_TYPE_BOOLEAN,
+ NGHTTP3_SF_VALUE_TYPE_INTEGER,
+ NGHTTP3_SF_VALUE_TYPE_DECIMAL,
+ NGHTTP3_SF_VALUE_TYPE_STRING,
+ NGHTTP3_SF_VALUE_TYPE_TOKEN,
+ NGHTTP3_SF_VALUE_TYPE_BYTESEQ,
+ NGHTTP3_SF_VALUE_TYPE_INNER_LIST,
+} nghttp3_sf_value_type;
+
+/*
+ * nghttp3_sf_value stores Structured Field Values item. For Inner
+ * List, only type is set to NGHTTP3_SF_VALUE_TYPE_INNER_LIST.
+ */
+typedef struct nghttp3_sf_value {
+ uint8_t type;
+ union {
+ int b;
+ int64_t i;
+ double d;
+ struct {
+ const uint8_t *base;
+ size_t len;
+ } s;
+ };
+} nghttp3_sf_value;
+
+/*
+ * nghttp3_sf_parse_item parses the input sequence [|begin|, |end|)
+ * and stores the parsed an Item in |dest|. It returns the number of
+ * bytes consumed if it succeeds, or -1. This function is declared
+ * here for unit tests.
+ */
+nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end);
+
+/*
+ * nghttp3_sf_parse_inner_list parses the input sequence [|begin|, |end|)
+ * and stores the parsed an Inner List in |dest|. It returns the number of
+ * bytes consumed if it succeeds, or -1. This function is declared
+ * here for unit tests.
+ */
+nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end);
+
#endif /* NGHTTP3_HTTP_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c
index cd8fd82e6b2..dc34841fe0f 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c
@@ -27,18 +27,10 @@
#include <assert.h>
-int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) {
- int rv;
-
- rv = nghttp3_gaptr_init(&idtr->gap, mem);
- if (rv != 0) {
- return rv;
- }
+void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) {
+ nghttp3_gaptr_init(&idtr->gap, mem);
idtr->server = server;
- idtr->mem = mem;
-
- return 0;
}
void nghttp3_idtr_free(nghttp3_idtr *idtr) {
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h
index c4d60861ab1..ea3346c9a96 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h
@@ -45,24 +45,15 @@ typedef struct nghttp3_idtr {
/* server is nonzero if this object records server initiated stream
ID. */
int server;
- /* mem is custom memory allocator */
- const nghttp3_mem *mem;
} nghttp3_idtr;
/*
- * nghttp3_idtr_init initializes |idtr|. |chunk| is the size of buffer
- * per chunk.
+ * nghttp3_idtr_init initializes |idtr|.
*
* If this object records server initiated ID (even number), set
* |server| to nonzero.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGHTTP3_ERR_NOMEM
- * Out of memory.
*/
-int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem);
+void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem);
/*
* nghttp3_idtr_free frees resources allocated for |idtr|.
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c
index 0896cb693f9..adea677abe1 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c
@@ -34,9 +34,11 @@
#include "nghttp3_mem.h"
#include "nghttp3_range.h"
+static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
+
static size_t ksl_nodelen(size_t keylen) {
- return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
- (size_t)~0xf;
+ return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) &
+ ~(uintptr_t)0xfu;
}
static size_t ksl_blklen(size_t nodelen) {
@@ -52,31 +54,48 @@ static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node,
memcpy(node->key, key, ksl->keylen);
}
-int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen,
- const nghttp3_mem *mem) {
+void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
+ size_t keylen, const nghttp3_mem *mem) {
size_t nodelen = ksl_nodelen(keylen);
- size_t blklen = ksl_blklen(nodelen);
- nghttp3_ksl_blk *head;
- ksl->head = nghttp3_mem_malloc(mem, blklen);
- if (!ksl->head) {
- return NGHTTP3_ERR_NOMEM;
- }
- ksl->front = ksl->back = ksl->head;
+ nghttp3_objalloc_init(&ksl->blkalloc,
+ ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8,
+ mem);
+
+ ksl->head = NULL;
+ ksl->front = ksl->back = NULL;
ksl->compar = compar;
ksl->keylen = keylen;
ksl->nodelen = nodelen;
ksl->n = 0;
- ksl->mem = mem;
+}
+
+static nghttp3_ksl_blk *ksl_blk_objalloc_new(nghttp3_ksl *ksl) {
+ return nghttp3_objalloc_ksl_blk_len_get(&ksl->blkalloc,
+ ksl_blklen(ksl->nodelen));
+}
+
+static void ksl_blk_objalloc_del(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ nghttp3_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
+}
+
+static int ksl_head_init(nghttp3_ksl *ksl) {
+ nghttp3_ksl_blk *head = ksl_blk_objalloc_new(ksl);
+ if (!head) {
+ return NGHTTP3_ERR_NOMEM;
+ }
- head = ksl->head;
head->next = head->prev = NULL;
head->n = 0;
head->leaf = 1;
+ ksl->head = head;
+ ksl->front = ksl->back = head;
+
return 0;
}
+#ifdef NOMEMPOOL
/*
* ksl_free_blk frees |blk| recursively.
*/
@@ -89,15 +108,20 @@ static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
}
}
- nghttp3_mem_free(ksl->mem, blk);
+ ksl_blk_objalloc_del(ksl, blk);
}
+#endif /* NOMEMPOOL */
void nghttp3_ksl_free(nghttp3_ksl *ksl) {
- if (!ksl) {
+ if (!ksl || !ksl->head) {
return;
}
+#ifdef NOMEMPOOL
ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ nghttp3_objalloc_free(&ksl->blkalloc);
}
/*
@@ -111,7 +135,7 @@ void nghttp3_ksl_free(nghttp3_ksl *ksl) {
static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
nghttp3_ksl_blk *rblk;
- rblk = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+ rblk = ksl_blk_objalloc_new(ksl);
if (rblk == NULL) {
return NULL;
}
@@ -197,9 +221,9 @@ static int ksl_split_head(nghttp3_ksl *ksl) {
lblk = ksl->head;
- nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+ nhead = ksl_blk_objalloc_new(ksl);
if (nhead == NULL) {
- nghttp3_mem_free(ksl->mem, rblk);
+ ksl_blk_objalloc_del(ksl, rblk);
return NGHTTP3_ERR_NOMEM;
}
nhead->next = nhead->prev = NULL;
@@ -246,29 +270,33 @@ static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i,
static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key,
nghttp3_ksl_compar compar) {
- nghttp3_ssize left = -1, right = (nghttp3_ssize)blk->n, mid;
+ size_t i;
nghttp3_ksl_node *node;
- while (right - left > 1) {
- mid = (left + right) >> 1;
- node = nghttp3_ksl_nth_node(ksl, blk, (size_t)mid);
- if (compar((nghttp3_ksl_key *)node->key, key)) {
- left = mid;
- } else {
- right = mid;
- }
- }
+ for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes;
+ i < blk->n && compar((nghttp3_ksl_key *)node->key, key);
+ ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
+ ;
- return (size_t)right;
+ return i;
}
int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
const nghttp3_ksl_key *key, void *data) {
- nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_blk *blk;
nghttp3_ksl_node *node;
size_t i;
int rv;
+ if (!ksl->head) {
+ rv = ksl_head_init(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ blk = ksl->head;
+
if (blk->n == NGHTTP3_KSL_MAX_NBLK) {
rv = ksl_split_head(ksl);
if (rv != 0) {
@@ -380,10 +408,10 @@ static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
ksl->back = lblk;
}
- nghttp3_mem_free(ksl->mem, rblk);
+ ksl_blk_objalloc_del(ksl, rblk);
if (ksl->head == blk && blk->n == 2) {
- nghttp3_mem_free(ksl->mem, ksl->head);
+ ksl_blk_objalloc_del(ksl, ksl->head);
ksl->head = lblk;
} else {
ksl_remove_node(ksl, blk, i + 1);
@@ -477,12 +505,42 @@ static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs,
return !compar(lhs, rhs) && !compar(rhs, lhs);
}
+int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_it *hint,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = hint->blk;
+
+ assert(ksl->head);
+
+ if (blk->n <= NGHTTP3_KSL_MIN_NBLK) {
+ return nghttp3_ksl_remove(ksl, it, key);
+ }
+
+ ksl_remove_node(ksl, blk, hint->i);
+
+ --ksl->n;
+
+ if (it) {
+ if (hint->i == blk->n && blk->next) {
+ nghttp3_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ nghttp3_ksl_it_init(it, ksl, blk, hint->i);
+ }
+ }
+
+ return 0;
+}
+
int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
const nghttp3_ksl_key *key) {
nghttp3_ksl_blk *blk = ksl->head;
nghttp3_ksl_node *node;
size_t i;
+ if (!ksl->head) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
if (!blk->leaf && blk->n == 2 &&
nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK &&
nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) {
@@ -558,6 +616,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
nghttp3_ksl_it it;
size_t i;
+ if (!blk) {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
for (;;) {
i = ksl_bsearch(ksl, blk, key, ksl->compar);
@@ -595,6 +658,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
nghttp3_ksl_it it;
size_t i;
+ if (!blk) {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
for (;;) {
i = ksl_bsearch(ksl, blk, key, compar);
@@ -631,6 +699,8 @@ void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
nghttp3_ksl_node *node;
size_t i;
+ assert(ksl->head);
+
for (;;) {
i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
@@ -675,36 +745,49 @@ static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) {
size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; }
void nghttp3_ksl_clear(nghttp3_ksl *ksl) {
- size_t i;
- nghttp3_ksl_blk *head;
-
- if (!ksl->head->leaf) {
- for (i = 0; i < ksl->head->n; ++i) {
- ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, ksl->head, i)->blk);
- }
+ if (!ksl->head) {
+ return;
}
- ksl->front = ksl->back = ksl->head;
- ksl->n = 0;
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
- head = ksl->head;
+ ksl->front = ksl->back = ksl->head = NULL;
+ ksl->n = 0;
- head->next = head->prev = NULL;
- head->n = 0;
- head->leaf = 1;
+ nghttp3_objalloc_clear(&ksl->blkalloc);
}
-void nghttp3_ksl_print(nghttp3_ksl *ksl) { ksl_print(ksl, ksl->head, 0); }
+void nghttp3_ksl_print(nghttp3_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+ ksl_print(ksl, ksl->head, 0);
+}
nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) {
nghttp3_ksl_it it;
- nghttp3_ksl_it_init(&it, ksl, ksl->front, 0);
+
+ if (ksl->head) {
+ nghttp3_ksl_it_init(&it, ksl, ksl->front, 0);
+ } else {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
return it;
}
nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) {
nghttp3_ksl_it it;
- nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+
+ if (ksl->head) {
+ nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+ } else {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
return it;
}
@@ -715,11 +798,6 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
it->i = i;
}
-void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) {
- assert(it->i < it->blk->n);
- return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data;
-}
-
void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) {
assert(!nghttp3_ksl_it_begin(it));
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h
index 7ba36bb9cb1..0bc10e846fe 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h
@@ -34,6 +34,8 @@
#include <nghttp3/nghttp3.h>
+#include "nghttp3_objalloc.h"
+
/*
* Skip List using single key instead of range.
*/
@@ -80,25 +82,34 @@ struct nghttp3_ksl_node {
* nghttp3_ksl_blk contains nghttp3_ksl_node objects.
*/
struct nghttp3_ksl_blk {
- /* next points to the next block if leaf field is nonzero. */
- nghttp3_ksl_blk *next;
- /* prev points to the previous block if leaf field is nonzero. */
- nghttp3_ksl_blk *prev;
- /* n is the number of nodes this object contains in nodes. */
- uint32_t n;
- /* leaf is nonzero if this block contains leaf nodes. */
- uint32_t leaf;
union {
- uint64_t align;
- /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK
- nghttp3_ksl_node objects. Because nghttp3_ksl_node object is
- allocated along with the additional variable length key
- storage, the size of buffer is unknown until nghttp3_ksl_init
- is called. */
- uint8_t nodes[1];
+ struct {
+ /* next points to the next block if leaf field is nonzero. */
+ nghttp3_ksl_blk *next;
+ /* prev points to the previous block if leaf field is
+ nonzero. */
+ nghttp3_ksl_blk *prev;
+ /* n is the number of nodes this object contains in nodes. */
+ uint32_t n;
+ /* leaf is nonzero if this block contains leaf nodes. */
+ uint32_t leaf;
+ union {
+ uint64_t align;
+ /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK
+ nghttp3_ksl_node objects. Because nghttp3_ksl_node object
+ is allocated along with the additional variable length key
+ storage, the size of buffer is unknown until
+ nghttp3_ksl_init is called. */
+ uint8_t nodes[1];
+ };
+ };
+
+ nghttp3_opl_entry oplent;
};
};
+nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent);
+
/*
* nghttp3_ksl_compar is a function type which returns nonzero if key
* |lhs| should be placed before |rhs|. It returns 0 otherwise.
@@ -123,6 +134,7 @@ struct nghttp3_ksl_it {
* nghttp3_ksl is a deterministic paged skip list.
*/
struct nghttp3_ksl {
+ nghttp3_objalloc blkalloc;
/* head points to the root block. */
nghttp3_ksl_blk *head;
/* front points to the first leaf block. */
@@ -136,21 +148,14 @@ struct nghttp3_ksl {
/* nodelen is the actual size of nghttp3_ksl_node including key
storage. */
size_t nodelen;
- const nghttp3_mem *mem;
};
/*
* nghttp3_ksl_init initializes |ksl|. |compar| specifies compare
* function. |keylen| is the length of key.
- *
- * It returns 0 if it succeeds, or one of the following negative error
- * codes:
- *
- * NGHTTP3_ERR_NOMEM
- * Out of memory.
*/
-int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen,
- const nghttp3_mem *mem);
+void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
+ size_t keylen, const nghttp3_mem *mem);
/*
* nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is
@@ -193,6 +198,17 @@ int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
const nghttp3_ksl_key *key);
/*
+ * nghttp3_ksl_remove_hint removes the |key| from |ksl|. |hint| must
+ * point to the same node denoted by |key|. |hint| is used to remove
+ * a node efficiently in some cases. Other than that, it behaves
+ * exactly like nghttp3_ksl_remove. |it| and |hint| can point to the
+ * same object.
+ */
+int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_it *hint,
+ const nghttp3_ksl_key *key);
+
+/*
* nghttp3_ksl_lower_bound returns the iterator which points to the
* first node which has the key which is equal to |key| or the last
* node which satisfies !compar(&node->key, key). If there is no such
@@ -267,7 +283,8 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
* |it| points to. It is undefined to call this function when
* nghttp3_ksl_it_end(it) returns nonzero.
*/
-void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it);
+#define nghttp3_ksl_it_get(IT) \
+ nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
/*
* nghttp3_ksl_it_next advances the iterator by one. It is undefined
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h
index 6ee704cc47d..a44e907661a 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h
@@ -44,4 +44,8 @@
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
+/* NGHTTP3_MAX_VARINT` is the maximum value which can be encoded in
+ variable-length integer encoding. */
+#define NGHTTP3_MAX_VARINT ((1ULL << 62) - 1)
+
#endif /* NGHTTP3_MACRO_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c
index a80955ad7a4..fcfc31ae41e 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c
@@ -28,172 +28,166 @@
#include <string.h>
#include <assert.h>
+#include <stdio.h>
#include "nghttp3_conv.h"
-#define INITIAL_TABLE_LENGTH 256
+#define NGHTTP3_INITIAL_TABLE_LENBITS 4
-int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) {
+void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) {
map->mem = mem;
- map->tablelen = INITIAL_TABLE_LENGTH;
- map->table =
- nghttp3_mem_calloc(mem, map->tablelen, sizeof(nghttp3_map_bucket));
- if (map->table == NULL) {
- return NGHTTP3_ERR_NOMEM;
- }
-
+ map->tablelen = 0;
+ map->tablelenbits = 0;
+ map->table = NULL;
map->size = 0;
-
- return 0;
}
void nghttp3_map_free(nghttp3_map *map) {
- size_t i;
- nghttp3_map_bucket *bkt;
-
if (!map) {
return;
}
- for (i = 0; i < map->tablelen; ++i) {
- bkt = &map->table[i];
- if (bkt->ksl) {
- nghttp3_ksl_free(bkt->ksl);
- nghttp3_mem_free(map->mem, bkt->ksl);
- }
- }
-
nghttp3_mem_free(map->mem, map->table);
}
-void nghttp3_map_each_free(nghttp3_map *map,
- int (*func)(nghttp3_map_entry *entry, void *ptr),
+void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
uint32_t i;
nghttp3_map_bucket *bkt;
- nghttp3_ksl_it it;
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
- if (bkt->ptr) {
- func(bkt->ptr, ptr);
- bkt->ptr = NULL;
- assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0);
+ if (bkt->data == NULL) {
continue;
}
- if (bkt->ksl) {
- for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it);
- nghttp3_ksl_it_next(&it)) {
- func(nghttp3_ksl_it_get(&it), ptr);
- }
-
- nghttp3_ksl_free(bkt->ksl);
- nghttp3_mem_free(map->mem, bkt->ksl);
- bkt->ksl = NULL;
- }
+ func(bkt->data, ptr);
}
}
-int nghttp3_map_each(nghttp3_map *map,
- int (*func)(nghttp3_map_entry *entry, void *ptr),
+int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
int rv;
uint32_t i;
nghttp3_map_bucket *bkt;
- nghttp3_ksl_it it;
+
+ if (map->size == 0) {
+ return 0;
+ }
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
- if (bkt->ptr) {
- rv = func(bkt->ptr, ptr);
- if (rv != 0) {
- return rv;
- }
- assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0);
+ if (bkt->data == NULL) {
continue;
}
- if (bkt->ksl) {
- for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it);
- nghttp3_ksl_it_next(&it)) {
- rv = func(nghttp3_ksl_it_get(&it), ptr);
- if (rv != 0) {
- return rv;
- }
- }
+ rv = func(bkt->data, ptr);
+ if (rv != 0) {
+ return rv;
}
}
+
return 0;
}
-void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) {
- entry->key = key;
+static uint32_t hash(nghttp3_map_key_type key) {
+ return (uint32_t)((key * 11400714819323198485llu) >> 32);
}
-/* FNV1a hash */
-static uint32_t hash(key_type key, uint32_t mod) {
- uint8_t *p, *end;
- uint32_t h = 0x811C9DC5u;
+static size_t h2idx(uint32_t hash, uint32_t bits) {
+ return hash >> (32 - bits);
+}
- key = nghttp3_htonl64(key);
- p = (uint8_t *)&key;
- end = p + sizeof(key_type);
+static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
+ nghttp3_map_bucket *bkt, size_t idx) {
+ return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
+}
- for (; p != end;) {
- h ^= *p++;
- h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
- }
+static void map_bucket_swap(nghttp3_map_bucket *bkt, uint32_t *phash,
+ nghttp3_map_key_type *pkey, void **pdata) {
+ uint32_t h = bkt->hash;
+ nghttp3_map_key_type key = bkt->key;
+ void *data = bkt->data;
- return h & (mod - 1);
+ bkt->hash = *phash;
+ bkt->key = *pkey;
+ bkt->data = *pdata;
+
+ *phash = h;
+ *pkey = key;
+ *pdata = data;
}
-static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) {
- return *(key_type *)lhs < *(key_type *)rhs;
+static void map_bucket_set_data(nghttp3_map_bucket *bkt, uint32_t hash,
+ nghttp3_map_key_type key, void *data) {
+ bkt->hash = hash;
+ bkt->key = key;
+ bkt->data = data;
}
-static int map_insert(nghttp3_map *map, nghttp3_map_bucket *table,
- uint32_t tablelen, nghttp3_map_entry *entry) {
- uint32_t h = hash(entry->key, tablelen);
- nghttp3_map_bucket *bkt = &table[h];
- const nghttp3_mem *mem = map->mem;
- int rv;
+void nghttp3_map_print_distance(nghttp3_map *map) {
+ uint32_t i;
+ size_t idx;
+ nghttp3_map_bucket *bkt;
- if (bkt->ptr == NULL &&
- (bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) {
- bkt->ptr = entry;
- return 0;
- }
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
- if (!bkt->ksl) {
- bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl));
- if (bkt->ksl == NULL) {
- return NGHTTP3_ERR_NOMEM;
+ if (bkt->data == NULL) {
+ fprintf(stderr, "@%u <EMPTY>\n", i);
+ continue;
}
- nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
+
+ idx = h2idx(bkt->hash, map->tablelenbits);
+ fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i,
+ bkt->hash, bkt->key, idx,
+ distance(map->tablelen, map->tablelenbits, bkt, idx));
}
+}
- if (bkt->ptr) {
- rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
- if (rv != 0) {
- return rv;
+static int insert(nghttp3_map_bucket *table, uint32_t tablelen,
+ uint32_t tablelenbits, uint32_t hash,
+ nghttp3_map_key_type key, void *data) {
+ size_t idx = h2idx(hash, tablelenbits);
+ size_t d = 0, dd;
+ nghttp3_map_bucket *bkt;
+
+ for (;;) {
+ bkt = &table[idx];
+
+ if (bkt->data == NULL) {
+ map_bucket_set_data(bkt, hash, key, data);
+ return 0;
}
- bkt->ptr = NULL;
- }
+ dd = distance(tablelen, tablelenbits, bkt, idx);
+ if (d > dd) {
+ map_bucket_swap(bkt, &hash, &key, &data);
+ d = dd;
+ } else if (bkt->key == key) {
+ /* TODO This check is just a waste after first swap or if this
+ function is called from map_resize. That said, there is no
+ difference with or without this conditional in performance
+ wise. */
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
- return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
+ ++d;
+ idx = (idx + 1) & (tablelen - 1);
+ }
}
-/* new_tablelen must be power of 2 */
-static int map_resize(nghttp3_map *map, uint32_t new_tablelen) {
+/* new_tablelen must be power of 2 and new_tablelen == (1 <<
+ new_tablelenbits) must hold. */
+static int map_resize(nghttp3_map *map, uint32_t new_tablelen,
+ uint32_t new_tablelenbits) {
uint32_t i;
nghttp3_map_bucket *new_table;
nghttp3_map_bucket *bkt;
- nghttp3_ksl_it it;
int rv;
+ (void)rv;
new_table =
nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket));
@@ -203,64 +197,46 @@ static int map_resize(nghttp3_map *map, uint32_t new_tablelen) {
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
-
- if (bkt->ptr) {
- rv = map_insert(map, new_table, new_tablelen, bkt->ptr);
- if (rv != 0) {
- goto fail;
- }
- assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0);
+ if (bkt->data == NULL) {
continue;
}
+ rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
+ bkt->data);
- if (bkt->ksl) {
- for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it);
- nghttp3_ksl_it_next(&it)) {
- rv = map_insert(map, new_table, new_tablelen, nghttp3_ksl_it_get(&it));
- if (rv != 0) {
- goto fail;
- }
- }
- }
- }
-
- for (i = 0; i < map->tablelen; ++i) {
- bkt = &map->table[i];
- if (bkt->ksl) {
- nghttp3_ksl_free(bkt->ksl);
- nghttp3_mem_free(map->mem, bkt->ksl);
- }
+ assert(0 == rv);
}
nghttp3_mem_free(map->mem, map->table);
map->tablelen = new_tablelen;
+ map->tablelenbits = new_tablelenbits;
map->table = new_table;
return 0;
-
-fail:
- for (i = 0; i < new_tablelen; ++i) {
- bkt = &new_table[i];
- if (bkt->ksl) {
- nghttp3_ksl_free(bkt->ksl);
- nghttp3_mem_free(map->mem, bkt->ksl);
- }
- }
-
- return rv;
}
-int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) {
+int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) {
int rv;
+ assert(data);
+
/* Load factor is 0.75 */
if ((map->size + 1) * 4 > map->tablelen * 3) {
- rv = map_resize(map, map->tablelen * 2);
- if (rv != 0) {
- return rv;
+ if (map->tablelen) {
+ rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ rv = map_resize(map, 1 << NGHTTP3_INITIAL_TABLE_LENBITS,
+ NGHTTP3_INITIAL_TABLE_LENBITS);
+ if (rv != 0) {
+ return rv;
+ }
}
}
- rv = map_insert(map, map->table, map->tablelen, new_entry);
+
+ rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
+ data);
if (rv != 0) {
return rv;
}
@@ -268,68 +244,93 @@ int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) {
return 0;
}
-nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key) {
- nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
- nghttp3_ksl_it it;
+void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key) {
+ uint32_t h;
+ size_t idx;
+ nghttp3_map_bucket *bkt;
+ size_t d = 0;
- if (bkt->ptr) {
- if (bkt->ptr->key == key) {
- return bkt->ptr;
- }
+ if (map->size == 0) {
return NULL;
}
- if (bkt->ksl) {
- it = nghttp3_ksl_lower_bound(bkt->ksl, &key);
- if (nghttp3_ksl_it_end(&it) ||
- *(key_type *)nghttp3_ksl_it_key(&it) != key) {
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
return NULL;
}
- return nghttp3_ksl_it_get(&it);
- }
- return NULL;
+ if (bkt->key == key) {
+ return bkt->data;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
}
-int nghttp3_map_remove(nghttp3_map *map, key_type key) {
- nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
- int rv;
+int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) {
+ uint32_t h;
+ size_t idx, didx;
+ nghttp3_map_bucket *bkt;
+ size_t d = 0;
- if (bkt->ptr) {
- if (bkt->ptr->key == key) {
- bkt->ptr = NULL;
- --map->size;
- return 0;
- }
+ if (map->size == 0) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
- if (bkt->ksl) {
- rv = nghttp3_ksl_remove(bkt->ksl, NULL, &key);
- if (rv != 0) {
- return rv;
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
}
- --map->size;
- return 0;
- }
- return NGHTTP3_ERR_INVALID_ARGUMENT;
-}
+ if (bkt->key == key) {
+ map_bucket_set_data(bkt, 0, 0, NULL);
-void nghttp3_map_clear(nghttp3_map *map) {
- uint32_t i;
- nghttp3_map_bucket *bkt;
+ didx = idx;
+ idx = (idx + 1) & (map->tablelen - 1);
- for (i = 0; i < map->tablelen; ++i) {
- bkt = &map->table[i];
- bkt->ptr = NULL;
- if (bkt->ksl) {
- nghttp3_ksl_free(bkt->ksl);
- nghttp3_mem_free(map->mem, bkt->ksl);
- bkt->ksl = NULL;
+ for (;;) {
+ bkt = &map->table[idx];
+ if (bkt->data == NULL ||
+ distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
+ break;
+ }
+
+ map->table[didx] = *bkt;
+ map_bucket_set_data(bkt, 0, 0, NULL);
+ didx = idx;
+
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+
+ --map->size;
+
+ return 0;
}
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+void nghttp3_map_clear(nghttp3_map *map) {
+ if (map->tablelen == 0) {
+ return;
}
+ memset(map->table, 0, sizeof(*map->table) * map->tablelen);
map->size = 0;
}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h
index 2eae2e7cb72..79dff0286bc 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h
@@ -34,21 +34,15 @@
#include <nghttp3/nghttp3.h>
#include "nghttp3_mem.h"
-#include "nghttp3_ksl.h"
/* Implementation of unordered map */
-typedef uint64_t key_type;
-
-typedef struct nghttp3_map_entry nghttp3_map_entry;
-
-struct nghttp3_map_entry {
- key_type key;
-};
+typedef uint64_t nghttp3_map_key_type;
typedef struct nghttp3_map_bucket {
- nghttp3_map_entry *ptr;
- nghttp3_ksl *ksl;
+ uint32_t hash;
+ nghttp3_map_key_type key;
+ void *data;
} nghttp3_map_bucket;
typedef struct nghttp3_map {
@@ -56,18 +50,13 @@ typedef struct nghttp3_map {
const nghttp3_mem *mem;
size_t size;
uint32_t tablelen;
+ uint32_t tablelenbits;
} nghttp3_map;
/*
* Initializes the map |map|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGHTTP3_ERR_NOMEM
- * Out of memory
*/
-int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem);
+void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem);
/*
* Deallocates any resources allocated for |map|. The stored entries
@@ -79,21 +68,14 @@ void nghttp3_map_free(nghttp3_map *map);
/*
* Deallocates each entries using |func| function and any resources
* allocated for |map|. The |func| function is responsible for freeing
- * given the |entry| object. The |ptr| will be passed to the |func| as
+ * given the |data| object. The |ptr| will be passed to the |func| as
* send argument. The return value of the |func| will be ignored.
*/
-void nghttp3_map_each_free(nghttp3_map *map,
- int (*func)(nghttp3_map_entry *entry, void *ptr),
+void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
void *ptr);
/*
- * Initializes the |entry| with the |key|. All entries to be inserted
- * to the map must be initialized with this function.
- */
-void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key);
-
-/*
- * Inserts the new |entry| with the key |entry->key| to the map |map|.
+ * Inserts the new |data| with the |key| to the map |map|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -103,25 +85,25 @@ void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key);
* NGHTTP3_ERR_NOMEM
* Out of memory
*/
-int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry);
+int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data);
/*
- * Returns the entry associated by the key |key|. If there is no such
- * entry, this function returns NULL.
+ * Returns the data associated by the key |key|. If there is no such
+ * data, this function returns NULL.
*/
-nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key);
+void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key);
/*
- * Removes the entry associated by the key |key| from the |map|. The
- * removed entry is not freed by this function.
+ * Removes the data associated by the key |key| from the |map|. The
+ * removed data is not freed by this function.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP3_ERR_INVALID_ARGUMENT
- * The entry associated by |key| does not exist.
+ * The data associated by |key| does not exist.
*/
-int nghttp3_map_remove(nghttp3_map *map, key_type key);
+int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key);
/*
* Removes all entries from |map|.
@@ -134,21 +116,22 @@ void nghttp3_map_clear(nghttp3_map *map);
size_t nghttp3_map_size(nghttp3_map *map);
/*
- * Applies the function |func| to each entry in the |map| with the
+ * Applies the function |func| to each data in the |map| with the
* optional user supplied pointer |ptr|.
*
* If the |func| returns 0, this function calls the |func| with the
- * next entry. If the |func| returns nonzero, it will not call the
+ * next data. If the |func| returns nonzero, it will not call the
* |func| for further entries and return the return value of the
* |func| immediately. Thus, this function returns 0 if all the
* invocations of the |func| return 0, or nonzero value which the last
* invocation of |func| returns.
*
- * Don't use this function to free each entry. Use
+ * Don't use this function to free each data. Use
* nghttp3_map_each_free() instead.
*/
-int nghttp3_map_each(nghttp3_map *map,
- int (*func)(nghttp3_map_entry *entry, void *ptr),
+int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
void *ptr);
+void nghttp3_map_print_distance(nghttp3_map *map);
+
#endif /* NGHTTP3_MAP_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c
index e5f93f10bda..0379e99b59c 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c
@@ -26,26 +26,28 @@
*/
#include "nghttp3_mem.h"
-static void *default_malloc(size_t size, void *mem_user_data) {
- (void)mem_user_data;
+#include <stdio.h>
+
+static void *default_malloc(size_t size, void *user_data) {
+ (void)user_data;
return malloc(size);
}
-static void default_free(void *ptr, void *mem_user_data) {
- (void)mem_user_data;
+static void default_free(void *ptr, void *user_data) {
+ (void)user_data;
free(ptr);
}
-static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
- (void)mem_user_data;
+static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
+ (void)user_data;
return calloc(nmemb, size);
}
-static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
- (void)mem_user_data;
+static void *default_realloc(void *ptr, size_t size, void *user_data) {
+ (void)user_data;
return realloc(ptr, size);
}
@@ -55,23 +57,68 @@ static nghttp3_mem mem_default = {NULL, default_malloc, default_free,
const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; }
+#ifndef MEMDEBUG
void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) {
- return mem->malloc(size, mem->mem_user_data);
+ return mem->malloc(size, mem->user_data);
}
void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) {
- mem->free(ptr, mem->mem_user_data);
-}
-
-void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr,
- void *mem_user_data) {
- free_func(ptr, mem_user_data);
+ mem->free(ptr, mem->user_data);
}
void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) {
- return mem->calloc(nmemb, size, mem->mem_user_data);
+ return mem->calloc(nmemb, size, mem->user_data);
}
void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) {
- return mem->realloc(ptr, size, mem->mem_user_data);
+ return mem->realloc(ptr, size, mem->user_data);
+}
+#else /* MEMDEBUG */
+void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->malloc(size, mem->user_data);
+
+ fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
+ file, line);
+
+ return nptr;
+}
+
+void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ mem->free(ptr, mem->user_data);
+}
+
+void nghttp3_mem_free2_debug(const nghttp3_free free_func, void *ptr,
+ void *user_data, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ free_func(ptr, user_data);
+}
+
+void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
+ size_t size, const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->calloc(nmemb, size, mem->user_data);
+
+ fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
+ size, func, file, line);
+
+ return nptr;
+}
+
+void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->realloc(ptr, size, mem->user_data);
+
+ fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
+ size, func, file, line);
+
+ return nptr;
}
+#endif /* MEMDEBUG */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h
index 55ef86b4f92..d6c3ada6f7e 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h
@@ -35,11 +35,46 @@
/* Convenient wrapper functions to call allocator function in
|mem|. */
+#ifndef MEMDEBUG
void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size);
void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr);
-void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr,
- void *mem_user_data);
void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size);
void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size);
+#else /* MEMDEBUG */
+void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define nghttp3_mem_malloc(MEM, SIZE) \
+ nghttp3_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
+
+void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line);
+
+# define nghttp3_mem_free(MEM, PTR) \
+ nghttp3_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
+
+void nghttp3_mem_free2_debug(nghttp3_free free_func, void *ptr, void *user_data,
+ const char *func, const char *file, size_t line);
+
+# define nghttp3_mem_free2(FREE_FUNC, PTR, USER_DATA) \
+ nghttp3_mem_free2_debug((FREE_FUNC), (PTR), (USER_DATA), __func__, \
+ __FILE__, __LINE__)
+
+void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
+ size_t size, const char *func, const char *file,
+ size_t line);
+
+# define nghttp3_mem_calloc(MEM, NMEMB, SIZE) \
+ nghttp3_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \
+ __LINE__)
+
+void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line);
+
+# define nghttp3_mem_realloc(MEM, PTR, SIZE) \
+ nghttp3_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, \
+ __LINE__)
+#endif /* MEMDEBUG */
#endif /* NGHTTP3_MEM_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c
new file mode 100644
index 00000000000..0c97860136e
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c
@@ -0,0 +1,41 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_objalloc.h"
+
+void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
+ const nghttp3_mem *mem) {
+ nghttp3_balloc_init(&objalloc->balloc, blklen, mem);
+ nghttp3_opl_init(&objalloc->opl);
+}
+
+void nghttp3_objalloc_free(nghttp3_objalloc *objalloc) {
+ nghttp3_balloc_free(&objalloc->balloc);
+}
+
+void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc) {
+ nghttp3_opl_clear(&objalloc->opl);
+ nghttp3_balloc_clear(&objalloc->balloc);
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h
new file mode 100644
index 00000000000..da39447a872
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h
@@ -0,0 +1,141 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_OBJALLOC_H
+#define NGHTTP3_OBJALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_balloc.h"
+#include "nghttp3_opl.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_mem.h"
+
+/*
+ * nghttp3_objalloc combines nghttp3_balloc and nghttp3_opl, and
+ * provides an object pool with the custom allocator to reduce the
+ * allocation and deallocation overheads for small objects.
+ */
+typedef struct nghttp3_objalloc {
+ nghttp3_balloc balloc;
+ nghttp3_opl opl;
+} nghttp3_objalloc;
+
+/*
+ * nghttp3_objalloc_init initializes |objalloc|. |blklen| is directly
+ * passed to nghttp3_balloc_init.
+ */
+void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_objalloc_free releases all allocated resources.
+ */
+void nghttp3_objalloc_free(nghttp3_objalloc *objalloc);
+
+/*
+ * nghttp3_objalloc_clear releases all allocated resources and
+ * initializes its state.
+ */
+void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc);
+
+#ifndef NOMEMPOOL
+# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void nghttp3_objalloc_##NAME##_init( \
+ nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
+ nghttp3_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_get( \
+ nghttp3_objalloc *objalloc) { \
+ nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, \
+ sizeof(TYPE)); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
+ nghttp3_objalloc *objalloc, size_t len) { \
+ nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, len); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static void nghttp3_objalloc_##NAME##_release( \
+ nghttp3_objalloc *objalloc, TYPE *obj) { \
+ nghttp3_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \
+ }
+#else /* NOMEMPOOL */
+# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void nghttp3_objalloc_##NAME##_init( \
+ nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
+ nghttp3_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_get( \
+ nghttp3_objalloc *objalloc) { \
+ return nghttp3_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
+ nghttp3_objalloc *objalloc, size_t len) { \
+ return nghttp3_mem_malloc(objalloc->balloc.mem, len); \
+ } \
+ \
+ inline static void nghttp3_objalloc_##NAME##_release( \
+ nghttp3_objalloc *objalloc, TYPE *obj) { \
+ nghttp3_mem_free(objalloc->balloc.mem, obj); \
+ }
+#endif /* NOMEMPOOL */
+
+#endif /* NGHTTP3_OBJALLOC_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c
new file mode 100644
index 00000000000..eb8ebdd1854
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c
@@ -0,0 +1,47 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_opl.h"
+
+void nghttp3_opl_init(nghttp3_opl *opl) { opl->head = NULL; }
+
+void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent) {
+ ent->next = opl->head;
+ opl->head = ent;
+}
+
+nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl) {
+ nghttp3_opl_entry *ent = opl->head;
+
+ if (!ent) {
+ return NULL;
+ }
+
+ opl->head = ent->next;
+
+ return ent;
+}
+
+void nghttp3_opl_clear(nghttp3_opl *opl) { opl->head = NULL; }
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h
new file mode 100644
index 00000000000..8c8a4f2051b
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h
@@ -0,0 +1,66 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_OPL_H
+#define NGHTTP3_OPL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_opl_entry nghttp3_opl_entry;
+
+struct nghttp3_opl_entry {
+ nghttp3_opl_entry *next;
+};
+
+/*
+ * nghttp3_opl is an object memory pool.
+ */
+typedef struct nghttp3_opl {
+ nghttp3_opl_entry *head;
+} nghttp3_opl;
+
+/*
+ * nghttp3_opl_init initializes |opl|.
+ */
+void nghttp3_opl_init(nghttp3_opl *opl);
+
+/*
+ * nghttp3_opl_push inserts |ent| to |opl| head.
+ */
+void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent);
+
+/*
+ * nghttp3_opl_pop removes the first nghttp3_opl_entry from |opl| and
+ * returns it. If |opl| does not have any entry, it returns NULL.
+ */
+nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl);
+
+void nghttp3_opl_clear(nghttp3_opl *opl);
+
+#endif /* NGHTTP3_OPL_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h
index f1f369231df..c1a54f505bb 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h
@@ -111,7 +111,7 @@ size_t nghttp3_pq_size(const nghttp3_pq *pq);
typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg);
/*
- * Applys |fun| to each item in |pq|. The |arg| is passed as arg
+ * Applies |fun| to each item in |pq|. The |arg| is passed as arg
* parameter to callback function. This function must not change the
* ordering key. If the return value from callback is nonzero, this
* function returns 1 immediately without iterating remaining items.
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c
index 6837942687a..ddb3dd6d84b 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c
@@ -168,8 +168,8 @@ static nghttp3_qpack_static_entry token_stable[] = {
/* Make scalar initialization form of nghttp3_qpack_static_entry */
#define MAKE_STATIC_HD(N, V, T) \
{ \
- {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
- {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \
+ {NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
+ {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \
}
static nghttp3_qpack_static_header stable[] = {
@@ -775,12 +775,12 @@ static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
/*
* qpack_context_can_reference returns nonzero if dynamic table entry
* at |absidx| can be referenced. In other words, it is within
- * ctx->max_dtable_size.
+ * ctx->max_dtable_capacity.
*/
static int qpack_context_can_reference(nghttp3_qpack_context *ctx,
uint64_t absidx) {
nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
- return ctx->dtable_sum - ent->sum <= ctx->max_dtable_size;
+ return ctx->dtable_sum - ent->sum <= ctx->max_dtable_capacity;
}
/* |*ppb_match| (post-base match), if it is not NULL, is always exact
@@ -824,8 +824,14 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
}
/*
- * qpack_context_init initializes |ctx|. |max_dtable_size| is the
- * maximum size of dynamic table. |mem| is a memory allocator.
+ * qpack_context_init initializes |ctx|. |hard_max_dtable_capacity|
+ * is the upper bound of the dynamic table capacity. |mem| is a
+ * memory allocator.
+ *
+ * The maximum dynamic table size is governed by
+ * ctx->max_dtable_capacity and it is initialized to 0.
+ * |hard_max_dtable_capacity| is the upper bound of
+ * ctx->max_dtable_capacity.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -834,7 +840,8 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
* Out of memory.
*/
static int qpack_context_init(nghttp3_qpack_context *ctx,
- size_t max_dtable_size, size_t max_blocked,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
const nghttp3_mem *mem) {
int rv;
size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD;
@@ -852,9 +859,9 @@ static int qpack_context_init(nghttp3_qpack_context *ctx,
ctx->mem = mem;
ctx->dtable_size = 0;
ctx->dtable_sum = 0;
- ctx->hard_max_dtable_size = max_dtable_size;
- ctx->max_dtable_size = 0;
- ctx->max_blocked = max_blocked;
+ ctx->hard_max_dtable_capacity = hard_max_dtable_capacity;
+ ctx->max_dtable_capacity = 0;
+ ctx->max_blocked_streams = max_blocked_streams;
ctx->next_absidx = 0;
ctx->bad = 0;
@@ -896,25 +903,19 @@ static int max_cnt_greater(const nghttp3_ksl_key *lhs,
}
int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
- size_t max_dtable_size, size_t max_blocked,
+ size_t hard_max_dtable_capacity,
const nghttp3_mem *mem) {
int rv;
- rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem);
+ rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem);
if (rv != 0) {
return rv;
}
- rv = nghttp3_map_init(&encoder->streams, mem);
- if (rv != 0) {
- goto streams_init_fail;
- }
+ nghttp3_map_init(&encoder->streams, mem);
- rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
- sizeof(nghttp3_blocked_streams_key), mem);
- if (rv != 0) {
- goto blocked_streams_init_fail;
- }
+ nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
+ sizeof(nghttp3_blocked_streams_key), mem);
qpack_map_init(&encoder->dtable_map);
nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem);
@@ -929,19 +930,11 @@ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_read_state_reset(&encoder->rstate);
return 0;
-
-blocked_streams_init_fail:
- nghttp3_map_free(&encoder->streams);
-streams_init_fail:
- qpack_context_free(&encoder->ctx);
-
- return rv;
}
-static int map_stream_free(nghttp3_map_entry *entry, void *ptr) {
+static int map_stream_free(void *data, void *ptr) {
const nghttp3_mem *mem = ptr;
- nghttp3_qpack_stream *stream =
- nghttp3_struct_of(entry, nghttp3_qpack_stream, me);
+ nghttp3_qpack_stream *stream = data;
nghttp3_qpack_stream_del(stream, mem);
return 0;
}
@@ -955,49 +948,27 @@ void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) {
qpack_context_free(&encoder->ctx);
}
-int nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder,
- size_t max_dtable_size) {
- if (encoder->ctx.hard_max_dtable_size < max_dtable_size) {
- return NGHTTP3_ERR_INVALID_ARGUMENT;
- }
+void nghttp3_qpack_encoder_set_max_dtable_capacity(
+ nghttp3_qpack_encoder *encoder, size_t max_dtable_capacity) {
+ max_dtable_capacity =
+ nghttp3_min(max_dtable_capacity, encoder->ctx.hard_max_dtable_capacity);
- if (encoder->ctx.max_dtable_size == max_dtable_size) {
- return 0;
+ if (encoder->ctx.max_dtable_capacity == max_dtable_capacity) {
+ return;
}
encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
- if (encoder->min_dtable_update > max_dtable_size) {
- encoder->min_dtable_update = max_dtable_size;
- encoder->ctx.max_dtable_size = max_dtable_size;
+ if (encoder->min_dtable_update > max_dtable_capacity) {
+ encoder->min_dtable_update = max_dtable_capacity;
+ encoder->ctx.max_dtable_capacity = max_dtable_capacity;
}
- encoder->last_max_dtable_update = max_dtable_size;
-
- return 0;
+ encoder->last_max_dtable_update = max_dtable_capacity;
}
-int nghttp3_qpack_encoder_set_hard_max_dtable_size(
- nghttp3_qpack_encoder *encoder, size_t hard_max_dtable_size) {
- /* TODO This is not ideal. */
- if (encoder->ctx.hard_max_dtable_size) {
- return NGHTTP3_ERR_INVALID_STATE;
- }
-
- encoder->ctx.hard_max_dtable_size = hard_max_dtable_size;
-
- return 0;
-}
-
-int nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder,
- size_t max_blocked) {
- /* TODO This is not ideal. */
- if (encoder->ctx.max_blocked) {
- return NGHTTP3_ERR_INVALID_STATE;
- }
-
- encoder->ctx.max_blocked = max_blocked;
-
- return 0;
+void nghttp3_qpack_encoder_set_max_blocked_streams(
+ nghttp3_qpack_encoder *encoder, size_t max_blocked_streams) {
+ encoder->ctx.max_blocked_streams = max_blocked_streams;
}
uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) {
@@ -1015,7 +986,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) {
size_t len;
nghttp3_qpack_entry *ent;
- if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) {
+ if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_capacity) {
return;
}
@@ -1023,7 +994,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) {
min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
}
- for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) {
+ for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_capacity;) {
len = nghttp3_ringbuf_len(dtable);
ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
if (ent->absidx + 1 == min_cnt) {
@@ -1069,7 +1040,8 @@ static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder,
assert(rv == NGHTTP3_ERR_NOMEM);
return rv;
}
- rv = nghttp3_map_insert(&encoder->streams, &stream->me);
+ rv = nghttp3_map_insert(&encoder->streams,
+ (nghttp3_map_key_type)stream->stream_id, stream);
if (rv != 0) {
assert(rv == NGHTTP3_ERR_NOMEM);
nghttp3_qpack_stream_del(stream, mem);
@@ -1110,7 +1082,8 @@ static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder,
size_t i, len;
nghttp3_qpack_header_block_ref *ref;
- nghttp3_map_remove(&encoder->streams, stream->me.key);
+ nghttp3_map_remove(&encoder->streams,
+ (nghttp3_map_key_type)stream->stream_id);
len = nghttp3_ringbuf_len(&stream->refs);
for (i = 0; i < len; ++i) {
@@ -1190,8 +1163,8 @@ int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder,
blocked_stream =
stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream);
allow_blocking =
- blocked_stream ||
- encoder->ctx.max_blocked > nghttp3_ksl_len(&encoder->blocked_streams);
+ blocked_stream || encoder->ctx.max_blocked_streams >
+ nghttp3_ksl_len(&encoder->blocked_streams);
DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id,
blocked_stream, allow_blocking);
@@ -1265,7 +1238,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_encoder_shrink_dtable(encoder);
- if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size ||
+ if (encoder->ctx.max_dtable_capacity < encoder->ctx.dtable_size ||
!(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) {
return 0;
}
@@ -1286,7 +1259,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
encoder->min_dtable_update = SIZE_MAX;
- encoder->ctx.max_dtable_size = encoder->last_max_dtable_update;
+ encoder->ctx.max_dtable_capacity = encoder->last_max_dtable_update;
return 0;
}
@@ -1300,9 +1273,7 @@ int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_stream *
nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder,
int64_t stream_id) {
- nghttp3_map_entry *me =
- nghttp3_map_find(&encoder->streams, (uint64_t)stream_id);
- return me == NULL ? NULL : nghttp3_struct_of(me, nghttp3_qpack_stream, me);
+ return nghttp3_map_find(&encoder->streams, (nghttp3_map_key_type)stream_id);
}
int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
@@ -1352,7 +1323,7 @@ qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder,
}
if (table_space(nv->namelen, nv->valuelen) >
- encoder->ctx.max_dtable_size * 3 / 4) {
+ encoder->ctx.max_dtable_capacity * 3 / 4) {
return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
}
@@ -1372,8 +1343,8 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need,
nghttp3_qpack_entry *min_ent, *last_ent;
nghttp3_ringbuf *dtable = &encoder->ctx.dtable;
- if (encoder->ctx.max_dtable_size > encoder->ctx.dtable_size) {
- avail = encoder->ctx.max_dtable_size - encoder->ctx.dtable_size;
+ if (encoder->ctx.max_dtable_capacity > encoder->ctx.dtable_size) {
+ avail = encoder->ctx.max_dtable_capacity - encoder->ctx.dtable_size;
if (need <= avail) {
return 1;
}
@@ -1385,7 +1356,7 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need,
}
if (min_cnt == UINT64_MAX) {
- return encoder->ctx.max_dtable_size >= need;
+ return encoder->ctx.max_dtable_capacity >= need;
}
min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1);
@@ -1434,8 +1405,8 @@ static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder,
*/
static int qpack_context_check_draining(nghttp3_qpack_context *ctx,
uint64_t absidx) {
- const size_t safe =
- ctx->max_dtable_size - nghttp3_min(512, ctx->max_dtable_size * 1 / 8);
+ const size_t safe = ctx->max_dtable_capacity -
+ nghttp3_min(512, ctx->max_dtable_capacity * 1 / 8);
nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
return ctx->dtable_sum - ent->sum > safe;
@@ -1714,7 +1685,7 @@ int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem);
- stream->me.key = (uint64_t)stream_id;
+ stream->stream_id = stream_id;
*pstream = stream;
@@ -2059,9 +2030,9 @@ int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
space = table_space(qnv->name->len, qnv->value->len);
- assert(space <= ctx->max_dtable_size);
+ assert(space <= ctx->max_dtable_capacity);
- while (ctx->dtable_size + space > ctx->max_dtable_size) {
+ while (ctx->dtable_size + space > ctx->max_dtable_capacity) {
i = nghttp3_ringbuf_len(&ctx->dtable);
assert(i);
ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
@@ -2265,7 +2236,7 @@ int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
nghttp3_qpack_header_block_ref, max_cnts_pe)
->max_cnt,
- stream->me.key};
+ (uint64_t)stream->stream_id};
return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream);
}
@@ -2276,7 +2247,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
nghttp3_qpack_header_block_ref, max_cnts_pe)
->max_cnt,
- stream->me.key};
+ (uint64_t)stream->stream_id};
nghttp3_ksl_it it;
/* This is purely debugging purpose only */
@@ -2285,7 +2256,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
assert(!nghttp3_ksl_it_end(&it));
assert(nghttp3_ksl_it_get(&it) == stream);
- nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &bsk);
+ nghttp3_ksl_remove_hint(&encoder->blocked_streams, NULL, &it, &bsk);
}
void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
@@ -2297,21 +2268,19 @@ void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
for (; !nghttp3_ksl_it_end(&it);) {
bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it);
- nghttp3_ksl_remove(&encoder->blocked_streams, &it, &bsk);
+ nghttp3_ksl_remove_hint(&encoder->blocked_streams, &it, &it, &bsk);
}
}
-void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
- int64_t stream_id) {
+int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id) {
nghttp3_qpack_stream *stream =
nghttp3_qpack_encoder_find_stream(encoder, stream_id);
const nghttp3_mem *mem = encoder->ctx.mem;
nghttp3_qpack_header_block_ref *ref;
if (stream == NULL) {
- /* This might be NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR, but we
- don't create stream which does not use dynamic table. */
- return;
+ return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
}
assert(nghttp3_ringbuf_len(&stream->refs));
@@ -2338,16 +2307,17 @@ void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_header_block_ref_del(ref, mem);
if (nghttp3_ringbuf_len(&stream->refs)) {
- return;
+ return 0;
}
qpack_encoder_remove_stream(encoder, stream);
nghttp3_qpack_stream_del(stream, mem);
+
+ return 0;
}
-int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder,
- uint64_t n) {
+int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n) {
if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) {
return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
}
@@ -2365,6 +2335,7 @@ void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) {
nghttp3_pq_clear(&encoder->min_cnts);
nghttp3_map_each_free(&encoder->streams, map_stream_free,
(void *)encoder->ctx.mem);
+ nghttp3_map_clear(&encoder->streams);
}
void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
@@ -2386,7 +2357,8 @@ void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_stream_del(stream, mem);
}
-size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) {
+size_t
+nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder) {
return nghttp3_ksl_len(&encoder->blocked_streams);
}
@@ -2394,7 +2366,7 @@ int nghttp3_qpack_encoder_write_field_section_prefix(
nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt,
uint64_t base) {
size_t max_ents =
- encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ encoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1;
int sign = base < ricnt;
uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt;
@@ -2563,15 +2535,17 @@ nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder,
switch (encoder->opcode) {
case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT:
- rv = nghttp3_qpack_encoder_add_insert_count(encoder,
- encoder->rstate.left);
+ rv = nghttp3_qpack_encoder_add_icnt(encoder, encoder->rstate.left);
if (rv != 0) {
goto fail;
}
break;
case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK:
- nghttp3_qpack_encoder_ack_header(encoder,
- (int64_t)encoder->rstate.left);
+ rv = nghttp3_qpack_encoder_ack_header(encoder,
+ (int64_t)encoder->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
break;
case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL:
nghttp3_qpack_encoder_cancel_stream(encoder,
@@ -2661,11 +2635,13 @@ void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) {
}
int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
- size_t max_dtable_size, size_t max_blocked,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
const nghttp3_mem *mem) {
int rv;
- rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem);
+ rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity,
+ max_blocked_streams, mem);
if (rv != 0) {
return rv;
}
@@ -2741,7 +2717,7 @@ static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate,
/*
* qpack_decoder_validate_index checks rstate->absidx is acceptable.
*
- * It returns 0 if it suceeds, or one of the following negative error
+ * It returns 0 if it succeeds, or one of the following negative error
* codes:
*
* NGHTTP3_ERR_QPACK_FATAL
@@ -2843,14 +2819,14 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
}
if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) {
- if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) {
+ DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n",
+ decoder->rstate.left);
+ rv = nghttp3_qpack_decoder_set_max_dtable_capacity(
+ decoder, (size_t)decoder->rstate.left);
+ if (rv != 0) {
rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
goto fail;
}
- DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n",
- decoder->rstate.left);
- nghttp3_qpack_decoder_set_dtable_cap(decoder,
- (size_t)decoder->rstate.left);
decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
nghttp3_qpack_read_state_reset(&decoder->rstate);
@@ -3036,6 +3012,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
default:
/* Unreachable */
assert(0);
+ abort();
}
if (rv != 0) {
goto fail;
@@ -3070,6 +3047,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
default:
/* Unreachable */
assert(0);
+ abort();
}
if (rv != 0) {
goto fail;
@@ -3088,16 +3066,20 @@ fail:
return rv;
}
-void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder,
- size_t cap) {
+int nghttp3_qpack_decoder_set_max_dtable_capacity(
+ nghttp3_qpack_decoder *decoder, size_t max_dtable_capacity) {
nghttp3_qpack_entry *ent;
size_t i;
nghttp3_qpack_context *ctx = &decoder->ctx;
const nghttp3_mem *mem = ctx->mem;
- ctx->max_dtable_size = cap;
+ if (max_dtable_capacity > decoder->ctx.hard_max_dtable_capacity) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ ctx->max_dtable_capacity = max_dtable_capacity;
- while (ctx->dtable_size > cap) {
+ while (ctx->dtable_size > max_dtable_capacity) {
i = nghttp3_ringbuf_len(&ctx->dtable);
assert(i);
ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
@@ -3108,6 +3090,8 @@ void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder,
nghttp3_qpack_entry_free(ent);
nghttp3_mem_free(mem, ent);
}
+
+ return 0;
}
int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) {
@@ -3131,7 +3115,7 @@ int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) {
shd = &stable[decoder->rstate.absidx];
if (table_space(shd->name.len, decoder->rstate.value->len) >
- decoder->ctx.max_dtable_size) {
+ decoder->ctx.max_dtable_capacity) {
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
}
@@ -3155,7 +3139,7 @@ int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) {
ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
if (table_space(ent->nv.name->len, decoder->rstate.value->len) >
- decoder->ctx.max_dtable_size) {
+ decoder->ctx.max_dtable_capacity) {
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
}
@@ -3185,7 +3169,7 @@ int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) {
ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
if (table_space(ent->nv.name->len, ent->nv.value->len) >
- decoder->ctx.max_dtable_size) {
+ decoder->ctx.max_dtable_capacity) {
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
}
@@ -3210,7 +3194,7 @@ int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) {
(int)decoder->rstate.value->len, decoder->rstate.value->base);
if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) >
- decoder->ctx.max_dtable_size) {
+ decoder->ctx.max_dtable_capacity) {
return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
}
@@ -3328,7 +3312,7 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
}
if (sctx->dbase_sign) {
- if (sctx->ricnt < sctx->rstate.left) {
+ if (sctx->ricnt <= sctx->rstate.left) {
rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
goto fail;
}
@@ -3595,7 +3579,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
switch (sctx->opcode) {
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
- nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ if (rv != 0) {
+ goto fail;
+ }
+
break;
case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
@@ -3629,7 +3617,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
switch (sctx->opcode) {
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
- nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ if (rv != 0) {
+ goto fail;
+ }
+
break;
case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
@@ -3733,6 +3725,7 @@ void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
uint8_t *p;
uint64_t n = 0;
size_t len = 0;
+ (void)len;
if (decoder->written_icnt < decoder->ctx.next_absidx) {
n = decoder->ctx.next_absidx - decoder->written_icnt;
@@ -3789,7 +3782,8 @@ int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
return 0;
}
- max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ max_ents =
+ decoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
full = 2 * max_ents;
if (encricnt > full) {
@@ -3938,13 +3932,19 @@ qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder,
sctx->rstate.value = NULL;
}
-static void
+static int
qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder,
nghttp3_qpack_stream_context *sctx,
nghttp3_qpack_nv *nv) {
- nghttp3_qpack_entry *ent =
- nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
- (void)decoder;
+ nghttp3_qpack_entry *ent;
+
+ /* A broken encoder might change dtable capacity while processing
+ request stream instruction. Check the absidx again. */
+ if (qpack_decoder_validate_index(decoder, &sctx->rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
nv->name = ent->nv.name;
nv->value = sctx->rstate.value;
@@ -3955,11 +3955,13 @@ qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder,
nghttp3_rcbuf_incref(nv->name);
sctx->rstate.value = NULL;
+
+ return 0;
}
-void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
- nghttp3_qpack_stream_context *sctx,
- nghttp3_qpack_nv *nv) {
+int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
(void)decoder;
DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n",
@@ -3967,10 +3969,12 @@ void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
(int)sctx->rstate.value->len, sctx->rstate.value->base);
if (sctx->rstate.dynamic) {
- qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv);
- } else {
- qpack_decoder_emit_static_indexed_name(decoder, sctx, nv);
+ return qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv);
}
+
+ qpack_decoder_emit_static_indexed_name(decoder, sctx, nv);
+
+ return 0;
}
void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
@@ -3993,7 +3997,7 @@ void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
}
int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
- size_t max_dtable_size, size_t max_blocked,
+ size_t hard_max_dtable_capacity,
const nghttp3_mem *mem) {
int rv;
nghttp3_qpack_encoder *p;
@@ -4003,7 +4007,7 @@ int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
return NGHTTP3_ERR_NOMEM;
}
- rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem);
+ rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem);
if (rv != 0) {
return rv;
}
@@ -4057,7 +4061,8 @@ void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) {
}
int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
- size_t max_dtable_size, size_t max_blocked,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
const nghttp3_mem *mem) {
int rv;
nghttp3_qpack_decoder *p;
@@ -4067,7 +4072,8 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
return NGHTTP3_ERR_NOMEM;
}
- rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem);
+ rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity,
+ max_blocked_streams, mem);
if (rv != 0) {
return rv;
}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h
index 429c55a7808..804969e14d6 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h
@@ -118,7 +118,7 @@ void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref,
const nghttp3_mem *mem);
typedef struct nghttp3_qpack_stream {
- nghttp3_map_entry me;
+ int64_t stream_id;
/* refs is an array of pointer to nghttp3_qpack_header_block_ref in
the order of the time they are encoded. HTTP/3 allows multiple
header blocks (e.g., non-final response headers, final response
@@ -154,18 +154,15 @@ typedef struct nghttp3_qpack_context {
NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */
size_t dtable_size;
size_t dtable_sum;
- /* hard_max_dtable_size is the maximum size of dynamic table. In
- HTTP/3, it is notified by decoder as
- SETTINGS_QPACK_MAX_TABLE_CAPACITY. Any value lower than or equal
- to SETTINGS_QPACK_MAX_TABLE_CAPACITY is OK because encoder has
- the authority to decide how many entries are inserted into
- dynamic table. */
- size_t hard_max_dtable_size;
- /* max_dtable_size is the effective maximum size of dynamic table. */
- size_t max_dtable_size;
- /* max_blocked is the maximum number of stream which can be
+ /* hard_max_dtable_capacity is the upper bound of
+ max_dtable_capacity. */
+ size_t hard_max_dtable_capacity;
+ /* max_dtable_capacity is the maximum capacity of the dynamic
+ table. */
+ size_t max_dtable_capacity;
+ /* max_blocked_streams is the maximum number of stream which can be
blocked. */
- size_t max_blocked;
+ size_t max_blocked_streams;
/* next_absidx is the next absolute index for nghttp3_qpack_entry.
It is equivalent to insert count. */
uint64_t next_absidx;
@@ -218,10 +215,10 @@ typedef enum nghttp3_qpack_decoder_stream_opcode {
/* QPACK encoder flags */
/* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */
-#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00
+#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00u
/* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that
Set Dynamic Table Capacity is required. */
-#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01
+#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01u
struct nghttp3_qpack_encoder {
nghttp3_qpack_context ctx;
@@ -260,9 +257,8 @@ struct nghttp3_qpack_encoder {
/*
* nghttp3_qpack_encoder_init initializes |encoder|.
- * |max_dtable_size| is the maximum size of dynamic table.
- * |max_blocked| is the maximum number of stream which can be blocked.
- * |mem| is a memory allocator.
+ * |hard_max_dtable_capacity| is the upper bound of the dynamic table
+ * capacity. |mem| is a memory allocator.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -271,7 +267,7 @@ struct nghttp3_qpack_encoder {
* Out of memory.
*/
int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
- size_t max_dtable_size, size_t max_blocked,
+ size_t hard_max_dtable_capacity,
const nghttp3_mem *mem);
/*
@@ -629,6 +625,44 @@ int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
int32_t token, uint32_t hash);
/*
+ * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header
+ * block for a stream denoted by |stream_id| was acknowledged by
+ * decoder.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
+ * Section Acknowledgement for a stream denoted by |stream_id| is
+ * unexpected.
+ */
+int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+/*
+ * `nghttp3_qpack_encoder_add_icnt` increments known received count of
+ * |encoder| by |n|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
+ * |n| is too large.
+ */
+int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n);
+
+/*
+ * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream
+ * denoted by |stream_id| is cancelled. This function is provided for
+ * debugging purpose only. In HTTP/3, |encoder| knows this by reading
+ * decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
+ */
+void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+/*
* nghttp3_qpack_context_dtable_get returns dynamic table entry whose
* absolute index is |absidx|. This function assumes that such entry
* exists.
@@ -749,9 +783,9 @@ struct nghttp3_qpack_decoder {
/*
* nghttp3_qpack_decoder_init initializes |decoder|.
- * |max_dtable_size| is the maximum size of dynamic table.
- * |max_blocked| is the maximum number of stream which can be blocked.
- * |mem| is a memory allocator.
+ * |hard_max_dtable_capacity| is the upper bound of the dynamic table
+ * capacity. |max_blocked_streams| is the maximum number of stream
+ * which can be blocked. |mem| is a memory allocator.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -760,7 +794,8 @@ struct nghttp3_qpack_decoder {
* Out of memory.
*/
int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
- size_t max_dtable_size, size_t max_blocked,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
const nghttp3_mem *mem);
/*
@@ -935,9 +970,9 @@ void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder,
nghttp3_qpack_stream_context *sctx,
nghttp3_qpack_nv *nv);
-void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
- nghttp3_qpack_stream_context *sctx,
- nghttp3_qpack_nv *nv);
+int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
nghttp3_qpack_stream_context *sctx,
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c
index 1c31ecebf60..9e9dab51390 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c
@@ -41,8 +41,7 @@ int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
*rcbuf_ptr = (void *)p;
- (*rcbuf_ptr)->mem_user_data = mem->mem_user_data;
- (*rcbuf_ptr)->free = mem->free;
+ (*rcbuf_ptr)->mem = mem;
(*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf);
(*rcbuf_ptr)->len = size;
(*rcbuf_ptr)->ref = 1;
@@ -76,7 +75,7 @@ int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
* Frees |rcbuf| itself, regardless of its reference cout.
*/
void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) {
- nghttp3_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data);
+ nghttp3_mem_free(rcbuf->mem, rcbuf);
}
void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) {
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h
index feea8040005..f589c377bf6 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h
@@ -33,10 +33,9 @@
#include <nghttp3/nghttp3.h>
struct nghttp3_rcbuf {
- /* custom memory allocator belongs to the mem parameter when
- creating this object. */
- void *mem_user_data;
- nghttp3_free free;
+ /* mem is the memory allocator that allocates memory for this
+ object. */
+ const nghttp3_mem *mem;
/* The pointer to the underlying buffer */
uint8_t *base;
/* Size of buffer pointed by |base|. */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c
index 96d60fe82f9..e655a7ec01d 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c
@@ -34,6 +34,7 @@
#include "nghttp3_conn.h"
#include "nghttp3_str.h"
#include "nghttp3_http.h"
+#include "nghttp3_vec.h"
/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which
makes a copy to outq. */
@@ -44,45 +45,36 @@
int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
uint64_t seq, const nghttp3_stream_callbacks *callbacks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ nghttp3_objalloc *stream_objalloc,
const nghttp3_mem *mem) {
- int rv;
- nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream));
+ nghttp3_stream *stream = nghttp3_objalloc_stream_get(stream_objalloc);
nghttp3_node_id nid;
if (stream == NULL) {
return NGHTTP3_ERR_NOMEM;
}
+ memset(stream, 0, sizeof(*stream));
+
+ stream->out_chunk_objalloc = out_chunk_objalloc;
+ stream->stream_objalloc = stream_objalloc;
+
nghttp3_tnode_init(
&stream->node,
nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_STREAM, stream_id), seq,
NGHTTP3_DEFAULT_URGENCY);
- rv = nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem);
- if (rv != 0) {
- goto frq_init_fail;
- }
-
- rv = nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem);
- if (rv != 0) {
- goto chunks_init_fail;
- }
-
- rv = nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem);
- if (rv != 0) {
- goto outq_init_fail;
- }
-
- rv = nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem);
- if (rv != 0) {
- goto inq_init_fail;
- }
+ nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem);
+ nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem);
+ nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem);
+ nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem);
nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem);
- stream->me.key = (key_type)stream_id;
stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
stream->mem = mem;
+ stream->tx.offset = 0;
stream->rx.http.status_code = -1;
stream->rx.http.content_length = -1;
stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY;
@@ -95,17 +87,6 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
*pstream = stream;
return 0;
-
-inq_init_fail:
- nghttp3_ringbuf_free(&stream->outq);
-outq_init_fail:
- nghttp3_ringbuf_free(&stream->chunks);
-chunks_init_fail:
- nghttp3_ringbuf_free(&stream->frq);
-frq_init_fail:
- nghttp3_mem_free(mem, stream);
-
- return rv;
}
static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) {
@@ -134,6 +115,27 @@ static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) {
nghttp3_ringbuf_free(chunks);
}
+static void delete_out_chunks(nghttp3_ringbuf *chunks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ const nghttp3_mem *mem) {
+ nghttp3_buf *buf;
+ size_t i, len = nghttp3_ringbuf_len(chunks);
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(chunks, i);
+
+ if (nghttp3_buf_cap(buf) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ nghttp3_objalloc_chunk_release(out_chunk_objalloc,
+ (nghttp3_chunk *)(void *)buf->begin);
+ continue;
+ }
+
+ nghttp3_buf_free(buf, mem);
+ }
+
+ nghttp3_ringbuf_free(chunks);
+}
+
static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) {
nghttp3_frame_entry *frent;
size_t i, len = nghttp3_ringbuf_len(frq);
@@ -144,9 +146,6 @@ static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) {
case NGHTTP3_FRAME_HEADERS:
nghttp3_frame_headers_free(&frent->fr.headers, mem);
break;
- case NGHTTP3_FRAME_PUSH_PROMISE:
- nghttp3_frame_push_promise_free(&frent->fr.push_promise, mem);
- break;
default:
break;
}
@@ -163,11 +162,11 @@ void nghttp3_stream_del(nghttp3_stream *stream) {
nghttp3_qpack_stream_context_free(&stream->qpack_sctx);
delete_chunks(&stream->inq, stream->mem);
delete_outq(&stream->outq, stream->mem);
- delete_chunks(&stream->chunks, stream->mem);
+ delete_out_chunks(&stream->chunks, stream->out_chunk_objalloc, stream->mem);
delete_frq(&stream->frq, stream->mem);
nghttp3_tnode_free(&stream->node);
- nghttp3_mem_free(stream->mem, stream);
+ nghttp3_objalloc_stream_release(stream->stream_objalloc, stream);
}
void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) {
@@ -267,19 +266,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
}
nghttp3_frame_headers_free(&frent->fr.headers, stream->mem);
break;
- case NGHTTP3_FRAME_PUSH_PROMISE:
- rv = nghttp3_stream_write_push_promise(stream, frent);
- if (rv != 0) {
- return rv;
- }
- nghttp3_frame_push_promise_free(&frent->fr.push_promise, stream->mem);
- break;
- case NGHTTP3_FRAME_CANCEL_PUSH:
- rv = nghttp3_stream_write_cancel_push(stream, frent);
- if (rv != 0) {
- return rv;
- }
- break;
case NGHTTP3_FRAME_DATA:
rv = nghttp3_stream_write_data(stream, &data_eof, frent);
if (rv != 0) {
@@ -292,11 +278,14 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
return 0;
}
break;
- case NGHTTP3_FRAME_MAX_PUSH_ID:
- if (stream->conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) {
- break;
+ case NGHTTP3_FRAME_GOAWAY:
+ rv = nghttp3_stream_write_goaway(stream, frent);
+ if (rv != 0) {
+ return rv;
}
- rv = nghttp3_stream_write_max_push_id(stream, frent);
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ rv = nghttp3_stream_write_priority_update(stream, frent);
if (rv != 0) {
return rv;
}
@@ -338,34 +327,6 @@ int nghttp3_stream_write_stream_type(nghttp3_stream *stream) {
return nghttp3_stream_outq_add(stream, &tbuf);
}
-int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream) {
- size_t len;
- nghttp3_buf *chunk;
- nghttp3_typed_buf tbuf;
- int rv;
- nghttp3_push_promise *pp = stream->pp;
-
- assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH);
- assert(pp);
-
- len = nghttp3_put_varint_len((int64_t)stream->type) +
- nghttp3_put_varint_len(pp->node.nid.id);
-
- rv = nghttp3_stream_ensure_chunk(stream, len);
- if (rv != 0) {
- return rv;
- }
-
- chunk = nghttp3_stream_get_chunk(stream);
- typed_buf_shared_init(&tbuf, chunk);
-
- chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type);
- chunk->last = nghttp3_put_varint(chunk->last, pp->node.nid.id);
- tbuf.buf.last = chunk->last;
-
- return nghttp3_stream_outq_add(stream, &tbuf);
-}
-
int nghttp3_stream_write_settings(nghttp3_stream *stream,
nghttp3_frame_entry *frent) {
size_t len;
@@ -386,10 +347,16 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream,
iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE;
iv[0].value = local_settings->max_field_section_size;
iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
- iv[1].value = local_settings->qpack_max_table_capacity;
+ iv[1].value = local_settings->qpack_max_dtable_capacity;
iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
iv[2].value = local_settings->qpack_blocked_streams;
+ if (local_settings->enable_connect_protocol) {
+ ++fr.settings.niv;
+ iv[3].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL;
+ iv[3].value = 1;
+ }
+
len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings);
rv = nghttp3_stream_ensure_chunk(stream, len);
@@ -407,15 +374,15 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream,
return nghttp3_stream_outq_add(stream, &tbuf);
}
-int nghttp3_stream_write_cancel_push(nghttp3_stream *stream,
- nghttp3_frame_entry *frent) {
- nghttp3_frame_cancel_push *fr = &frent->fr.cancel_push;
+int nghttp3_stream_write_goaway(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_goaway *fr = &frent->fr.goaway;
size_t len;
int rv;
nghttp3_buf *chunk;
nghttp3_typed_buf tbuf;
- len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr);
+ len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr);
rv = nghttp3_stream_ensure_chunk(stream, len);
if (rv != 0) {
@@ -425,30 +392,22 @@ int nghttp3_stream_write_cancel_push(nghttp3_stream *stream,
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
- chunk->last = nghttp3_frame_write_cancel_push(chunk->last, fr);
+ chunk->last = nghttp3_frame_write_goaway(chunk->last, fr);
tbuf.buf.last = chunk->last;
return nghttp3_stream_outq_add(stream, &tbuf);
}
-int nghttp3_stream_write_max_push_id(nghttp3_stream *stream,
- nghttp3_frame_entry *frent) {
- nghttp3_frame_max_push_id *fr = &frent->fr.max_push_id;
- nghttp3_conn *conn = stream->conn;
+int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_priority_update *fr = &frent->fr.priority_update;
size_t len;
int rv;
nghttp3_buf *chunk;
nghttp3_typed_buf tbuf;
- assert(conn);
- assert(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED);
-
- fr->push_id = (int64_t)conn->remote.uni.unsent_max_pushes - 1;
- conn->remote.uni.max_pushes = conn->remote.uni.unsent_max_pushes;
- conn->flags &= (uint16_t)~NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED;
-
- len = nghttp3_frame_write_max_push_id_len(&fr->hd.length, fr);
+ len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr);
rv = nghttp3_stream_ensure_chunk(stream, len);
if (rv != 0) {
@@ -458,7 +417,7 @@ int nghttp3_stream_write_max_push_id(nghttp3_stream *stream,
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
- chunk->last = nghttp3_frame_write_max_push_id(chunk->last, fr);
+ chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr);
tbuf.buf.last = chunk->last;
@@ -474,35 +433,21 @@ int nghttp3_stream_write_headers(nghttp3_stream *stream,
return nghttp3_stream_write_header_block(
stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf,
- &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, 0, fr->nva, fr->nvlen);
-}
-
-int nghttp3_stream_write_push_promise(nghttp3_stream *stream,
- nghttp3_frame_entry *frent) {
- nghttp3_frame_push_promise *fr = &frent->fr.push_promise;
- nghttp3_conn *conn = stream->conn;
-
- assert(conn);
-
- return nghttp3_stream_write_header_block(
- stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf,
- &conn->tx.qpack.ebuf, NGHTTP3_FRAME_PUSH_PROMISE, fr->push_id, fr->nva,
- fr->nvlen);
+ &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, fr->nva, fr->nvlen);
}
int nghttp3_stream_write_header_block(nghttp3_stream *stream,
nghttp3_qpack_encoder *qenc,
nghttp3_stream *qenc_stream,
nghttp3_buf *rbuf, nghttp3_buf *ebuf,
- int64_t frame_type, int64_t push_id,
- const nghttp3_nv *nva, size_t nvlen) {
+ int64_t frame_type, const nghttp3_nv *nva,
+ size_t nvlen) {
nghttp3_buf pbuf;
int rv;
size_t len;
nghttp3_buf *chunk;
nghttp3_typed_buf tbuf;
nghttp3_frame_hd hd;
- size_t push_idlen = 0;
uint8_t raw_pbuf[16];
size_t pbuflen, rbuflen, ebuflen;
@@ -518,14 +463,10 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
rbuflen = nghttp3_buf_len(rbuf);
ebuflen = nghttp3_buf_len(ebuf);
- if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) {
- push_idlen = nghttp3_put_varint_len(push_id);
- }
-
hd.type = frame_type;
- hd.length = (int64_t)(pbuflen + rbuflen + push_idlen);
+ hd.length = (int64_t)(pbuflen + rbuflen);
- len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen;
+ len = nghttp3_frame_write_hd_len(&hd) + pbuflen;
if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) {
len += rbuflen;
@@ -541,10 +482,6 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
- if (push_idlen) {
- chunk->last = nghttp3_put_varint(chunk->last, push_id);
- }
-
chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen);
nghttp3_buf_init(&pbuf);
@@ -623,7 +560,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
nghttp3_buf *chunk;
nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data;
nghttp3_conn *conn = stream->conn;
- size_t datalen;
+ int64_t datalen;
uint32_t flags = 0;
nghttp3_frame_hd hd;
nghttp3_vec vec[8];
@@ -647,7 +584,10 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
- datalen = nghttp3_vec_len(vec, (size_t)sveccnt);
+ datalen = nghttp3_vec_len_varint(vec, (size_t)sveccnt);
+ if (datalen == -1) {
+ return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
+ }
assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF);
@@ -677,7 +617,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
}
hd.type = NGHTTP3_FRAME_DATA;
- hd.length = (int64_t)datalen;
+ hd.length = datalen;
len = nghttp3_frame_write_hd_len(&hd);
@@ -762,8 +702,14 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream,
int rv;
nghttp3_typed_buf *dest;
size_t len = nghttp3_ringbuf_len(outq);
+ size_t buflen = nghttp3_buf_len(&tbuf->buf);
+
+ if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) {
+ return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
+ }
- stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf);
+ stream->tx.offset += buflen;
+ stream->unsent_bytes += buflen;
if (len) {
dest = nghttp3_ringbuf_get(outq, len - 1);
@@ -816,7 +762,12 @@ int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) {
for (; n < need; n *= 2)
;
- p = nghttp3_mem_malloc(stream->mem, n);
+ if (n == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ p = (uint8_t *)nghttp3_objalloc_chunk_len_get(stream->out_chunk_objalloc,
+ n);
+ } else {
+ p = nghttp3_mem_malloc(stream->mem, n);
+ }
if (p == NULL) {
return NGHTTP3_ERR_NOMEM;
}
@@ -863,7 +814,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
nghttp3_ringbuf *outq = &stream->outq;
size_t len = nghttp3_ringbuf_len(outq);
size_t i;
- size_t offset = stream->outq_offset;
+ uint64_t offset = stream->outq_offset;
size_t buflen;
nghttp3_vec *vbegin = vec, *vend = vec + veccnt;
nghttp3_typed_buf *tbuf;
@@ -879,7 +830,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
}
vec->base = tbuf->buf.pos + offset;
- vec->len = buflen - offset;
+ vec->len = (size_t)(buflen - offset);
++vec;
++i;
break;
@@ -903,7 +854,7 @@ int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) {
nghttp3_ringbuf *outq = &stream->outq;
size_t i;
size_t len = nghttp3_ringbuf_len(outq);
- size_t offset = stream->outq_offset + n;
+ uint64_t offset = stream->outq_offset + n;
size_t buflen;
nghttp3_typed_buf *tbuf;
@@ -945,7 +896,7 @@ static int stream_pop_outq_entry(nghttp3_stream *stream,
break;
case NGHTTP3_BUF_TYPE_ALIEN:
break;
- default:
+ case NGHTTP3_BUF_TYPE_SHARED:
assert(nghttp3_ringbuf_len(chunks));
chunk = nghttp3_ringbuf_get(chunks, 0);
@@ -954,9 +905,18 @@ static int stream_pop_outq_entry(nghttp3_stream *stream,
assert(chunk->end == tbuf->buf.end);
if (chunk->last == tbuf->buf.last) {
- nghttp3_buf_free(chunk, stream->mem);
+ if (nghttp3_buf_cap(chunk) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ nghttp3_objalloc_chunk_release(stream->out_chunk_objalloc,
+ (nghttp3_chunk *)(void *)chunk->begin);
+ } else {
+ nghttp3_buf_free(chunk, stream->mem);
+ }
nghttp3_ringbuf_pop_front(chunks);
}
+ break;
+ default:
+ assert(0);
+ abort();
};
nghttp3_ringbuf_pop_front(&stream->outq);
@@ -969,7 +929,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) {
uint64_t offset = stream->ack_offset + n;
size_t buflen;
size_t npopped = 0;
- size_t nack;
+ uint64_t nack;
nghttp3_typed_buf *tbuf;
int rv;
@@ -978,7 +938,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) {
buflen = nghttp3_buf_len(&tbuf->buf);
if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) {
- nack = (size_t)nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done;
+ nack = nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done;
if (stream->callbacks.acked_data) {
rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack,
stream->user_data);
@@ -1110,16 +1070,16 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
- rv = nghttp3_http_on_remote_end_stream(stream);
- if (rv != 0) {
- return rv;
- }
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
return 0;
case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN;
return 0;
case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
return 0;
default:
@@ -1141,13 +1101,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
rv = nghttp3_http_on_remote_end_stream(stream);
if (rv != 0) {
return rv;
}
- stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
- return 0;
- case NGHTTP3_HTTP_EVENT_MSG_END:
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
return 0;
default:
@@ -1165,6 +1125,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
spec. */
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
return 0;
case NGHTTP3_HTTP_STATE_REQ_END:
@@ -1192,10 +1156,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.http.status_code / 100 == 2) {
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
- rv = nghttp3_http_on_remote_end_stream(stream);
- if (rv != 0) {
- return rv;
- }
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
return 0;
case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
@@ -1205,6 +1165,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
return 0;
case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
return 0;
default:
@@ -1226,13 +1190,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.http.status_code / 100 == 2) {
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
rv = nghttp3_http_on_remote_end_stream(stream);
if (rv != 0) {
return rv;
}
- stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
- return 0;
- case NGHTTP3_HTTP_EVENT_MSG_END:
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
return 0;
default:
@@ -1248,12 +1212,17 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
}
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
return 0;
case NGHTTP3_HTTP_STATE_RESP_END:
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
default:
assert(0);
+ abort();
}
}
@@ -1267,11 +1236,6 @@ int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) {
}
}
-int nghttp3_stream_bidi_or_push(nghttp3_stream *stream) {
- return (!nghttp3_stream_uni(stream->node.nid.id) ||
- stream->type == NGHTTP3_STREAM_TYPE_PUSH);
-}
-
int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; }
int nghttp3_client_stream_bidi(int64_t stream_id) {
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h
index f7e375c85eb..06292738a17 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h
@@ -37,6 +37,7 @@
#include "nghttp3_buf.h"
#include "nghttp3_frame.h"
#include "nghttp3_qpack.h"
+#include "nghttp3_objalloc.h"
#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256
@@ -60,13 +61,14 @@ typedef enum nghttp3_stream_type {
typedef enum nghttp3_ctrl_stream_state {
NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE,
NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH,
- NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH,
NGHTTP3_CTRL_STREAM_STATE_SETTINGS,
NGHTTP3_CTRL_STREAM_STATE_GOAWAY,
NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID,
NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME,
NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID,
NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE,
+ NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID,
+ NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE,
} nghttp3_ctrl_stream_state;
typedef enum nghttp3_req_stream_state {
@@ -74,23 +76,10 @@ typedef enum nghttp3_req_stream_state {
NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH,
NGHTTP3_REQ_STREAM_STATE_DATA,
NGHTTP3_REQ_STREAM_STATE_HEADERS,
- NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID,
- NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE,
- NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE,
NGHTTP3_REQ_STREAM_STATE_IGN_FRAME,
NGHTTP3_REQ_STREAM_STATE_IGN_REST,
} nghttp3_req_stream_state;
-typedef enum nghttp3_push_stream_state {
- NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE,
- NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH,
- NGHTTP3_PUSH_STREAM_STATE_DATA,
- NGHTTP3_PUSH_STREAM_STATE_HEADERS,
- NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME,
- NGHTTP3_PUSH_STREAM_STATE_PUSH_ID,
- NGHTTP3_PUSH_STREAM_STATE_IGN_REST,
-} nghttp3_push_stream_state;
-
typedef struct nghttp3_varint_read_state {
int64_t acc;
size_t left;
@@ -104,38 +93,46 @@ typedef struct nghttp3_stream_read_state {
} nghttp3_stream_read_state;
/* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */
-#define NGHTTP3_STREAM_FLAG_NONE 0x0000
+#define NGHTTP3_STREAM_FLAG_NONE 0x0000u
/* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional
stream type is identified. */
-#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001
+#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001u
/* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by
QUIC flow control. */
-#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002
+#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002u
/* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is
temporarily unable to provide data. */
-#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004
+#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004u
/* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application
finished to feed outgoing data. */
-#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008
+#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008u
/* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is
blocked due to QPACK decoding. */
-#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010
+#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010u
/* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent
fin. */
-#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020
+#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020u
/* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed.
nghttp3_stream object can still alive because it might be blocked
by QPACK decoder. */
-#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040
-/* NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED indicates that stream is
- blocked because the corresponding PUSH_PROMISE has not been
- received yet. */
-#define NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED 0x0080
+#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040u
/* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write
operation to a stream is prohibited. */
-#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100
-/* NGHTTP3_STREAM_FLAG_RESET indicates that stream is reset. */
-#define NGHTTP3_STREAM_FLAG_RESET 0x0200
+#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100u
+/* NGHTTP3_STREAM_FLAG_SHUT_RD indicates that a read-side stream is
+ closed abruptly and any incoming and pending stream data is just
+ discarded for a stream. */
+#define NGHTTP3_STREAM_FLAG_SHUT_RD 0x0200u
+/* NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET indicates that server
+ overrides stream priority. */
+#define NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET 0x0400u
+/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server
+ received PRIORITY_UPDATE frame for this stream. */
+#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u
+/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while
+ processing incoming HTTP fields. */
+#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u
typedef enum nghttp3_stream_http_state {
NGHTTP3_HTTP_STATE_NONE,
@@ -164,15 +161,11 @@ typedef enum nghttp3_stream_http_event {
NGHTTP3_HTTP_EVENT_DATA_END,
NGHTTP3_HTTP_EVENT_HEADERS_BEGIN,
NGHTTP3_HTTP_EVENT_HEADERS_END,
- NGHTTP3_HTTP_EVENT_PUSH_PROMISE_BEGIN,
- NGHTTP3_HTTP_EVENT_PUSH_PROMISE_END,
NGHTTP3_HTTP_EVENT_MSG_END,
} nghttp3_stream_http_event;
typedef struct nghttp3_stream nghttp3_stream;
-typedef struct nghttp3_push_promise nghttp3_push_promise;
-
/*
* nghttp3_stream_acked_data is a callback function which is invoked
* when data sent on stream denoted by |stream_id| supplied from
@@ -185,7 +178,7 @@ typedef struct nghttp3_push_promise nghttp3_push_promise;
* NGHTTP3_ERR_CALLBACK_FAILURE.
*/
typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream,
- int64_t stream_id, size_t datalen,
+ int64_t stream_id, uint64_t datalen,
void *user_data);
typedef struct nghttp3_stream_callbacks {
@@ -202,65 +195,70 @@ typedef struct nghttp3_http_state {
/* recv_content_length is the number of body bytes received so
far. */
int64_t recv_content_length;
- uint16_t flags;
+ uint32_t flags;
/* pri is a stream priority produced by nghttp3_pri_to_uint8. */
uint8_t pri;
} nghttp3_http_state;
struct nghttp3_stream {
- const nghttp3_mem *mem;
- nghttp3_map_entry me;
- /* node is a node in dependency tree. For server initiated
- unidirectional stream (push), scheduling is done via
- corresponding nghttp3_push_promise object pointed by pp. */
- nghttp3_tnode node;
- nghttp3_pq_entry qpack_blocked_pe;
- nghttp3_stream_callbacks callbacks;
- nghttp3_ringbuf frq;
- nghttp3_ringbuf chunks;
- nghttp3_ringbuf outq;
- /* inq stores the stream raw data which cannot be read because
- stream is blocked by QPACK decoder. */
- nghttp3_ringbuf inq;
- nghttp3_qpack_stream_context qpack_sctx;
- /* conn is a reference to underlying connection. It could be NULL
- if stream is not a request/push stream. */
- nghttp3_conn *conn;
- void *user_data;
- /* unsent_bytes is the number of bytes in outq not written yet */
- size_t unsent_bytes;
- /* outq_idx is an index into outq where next write is made. */
- size_t outq_idx;
- /* outq_offset is write offset relative to the element at outq_idx
- in outq. */
- size_t outq_offset;
- /* ack_offset is offset acknowledged by peer relative to the first
- element in outq. */
- uint64_t ack_offset;
- /* ack_done is the number of bytes notified to an application that
- they are acknowledged inside the first outq element if it is of
- type NGHTTP3_BUF_TYPE_ALIEN. */
- size_t ack_done;
- size_t unscheduled_nwrite;
- nghttp3_stream_type type;
- nghttp3_stream_read_state rstate;
- /* pp is nghttp3_push_promise that this stream fulfills. */
- nghttp3_push_promise *pp;
- /* error_code indicates the reason of closure of this stream. */
- uint64_t error_code;
-
- struct {
- nghttp3_stream_http_state hstate;
- } tx;
-
- struct {
- nghttp3_stream_http_state hstate;
- nghttp3_http_state http;
- } rx;
-
- uint16_t flags;
+ union {
+ struct {
+ const nghttp3_mem *mem;
+ nghttp3_objalloc *out_chunk_objalloc;
+ nghttp3_objalloc *stream_objalloc;
+ nghttp3_tnode node;
+ nghttp3_pq_entry qpack_blocked_pe;
+ nghttp3_stream_callbacks callbacks;
+ nghttp3_ringbuf frq;
+ nghttp3_ringbuf chunks;
+ nghttp3_ringbuf outq;
+ /* inq stores the stream raw data which cannot be read because
+ stream is blocked by QPACK decoder. */
+ nghttp3_ringbuf inq;
+ nghttp3_qpack_stream_context qpack_sctx;
+ /* conn is a reference to underlying connection. It could be NULL
+ if stream is not a request stream. */
+ nghttp3_conn *conn;
+ void *user_data;
+ /* unsent_bytes is the number of bytes in outq not written yet */
+ uint64_t unsent_bytes;
+ /* outq_idx is an index into outq where next write is made. */
+ size_t outq_idx;
+ /* outq_offset is write offset relative to the element at outq_idx
+ in outq. */
+ uint64_t outq_offset;
+ /* ack_offset is offset acknowledged by peer relative to the first
+ element in outq. */
+ uint64_t ack_offset;
+ /* ack_done is the number of bytes notified to an application that
+ they are acknowledged inside the first outq element if it is of
+ type NGHTTP3_BUF_TYPE_ALIEN. */
+ uint64_t ack_done;
+ uint64_t unscheduled_nwrite;
+ nghttp3_stream_type type;
+ nghttp3_stream_read_state rstate;
+ /* error_code indicates the reason of closure of this stream. */
+ uint64_t error_code;
+
+ struct {
+ uint64_t offset;
+ nghttp3_stream_http_state hstate;
+ } tx;
+
+ struct {
+ nghttp3_stream_http_state hstate;
+ nghttp3_http_state http;
+ } rx;
+
+ uint16_t flags;
+ };
+
+ nghttp3_opl_entry oplent;
+ };
};
+nghttp3_objalloc_def(stream, nghttp3_stream, oplent);
+
typedef struct nghttp3_frame_entry {
nghttp3_frame fr;
union {
@@ -275,6 +273,8 @@ typedef struct nghttp3_frame_entry {
int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
uint64_t seq, const nghttp3_stream_callbacks *callbacks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ nghttp3_objalloc *stream_objalloc,
const nghttp3_mem *mem);
void nghttp3_stream_del(nghttp3_stream *stream);
@@ -293,8 +293,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream);
int nghttp3_stream_write_stream_type(nghttp3_stream *stream);
-int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream);
-
nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
nghttp3_vec *vec, size_t veccnt);
@@ -308,15 +306,12 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream,
int nghttp3_stream_write_headers(nghttp3_stream *stream,
nghttp3_frame_entry *frent);
-int nghttp3_stream_write_push_promise(nghttp3_stream *stream,
- nghttp3_frame_entry *frent);
-
int nghttp3_stream_write_header_block(nghttp3_stream *stream,
nghttp3_qpack_encoder *qenc,
nghttp3_stream *qenc_stream,
nghttp3_buf *rbuf, nghttp3_buf *ebuf,
- int64_t frame_type, int64_t push_id,
- const nghttp3_nv *nva, size_t nvlen);
+ int64_t frame_type, const nghttp3_nv *nva,
+ size_t nvlen);
int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
nghttp3_frame_entry *frent);
@@ -324,11 +319,11 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
int nghttp3_stream_write_settings(nghttp3_stream *stream,
nghttp3_frame_entry *frent);
-int nghttp3_stream_write_cancel_push(nghttp3_stream *stream,
- nghttp3_frame_entry *frent);
+int nghttp3_stream_write_goaway(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
-int nghttp3_stream_write_max_push_id(nghttp3_stream *stream,
- nghttp3_frame_entry *frent);
+int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need);
@@ -375,12 +370,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream);
/*
- * nghttp3_stream_bidi_or_push returns nonzero if |stream| is
- * bidirectional or push stream.
- */
-int nghttp3_stream_bidi_or_push(nghttp3_stream *stream);
-
-/*
* nghttp3_stream_uni returns nonzero if stream identified by
* |stream_id| is unidirectional.
*/
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c
index 94dca7dbf76..36e738c3469 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c
@@ -82,7 +82,7 @@ static uint64_t pq_get_first_cycle(nghttp3_pq *pq) {
}
int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
- size_t nwrite) {
+ uint64_t nwrite) {
uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN;
if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h
index 817aec034c0..f71dcf5ee31 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h
@@ -72,7 +72,8 @@ void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq);
* If |tnode| has already been scheduled, it is rescheduled by the
* amount of |nwrite|.
*/
-int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, size_t nwrite);
+int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
+ uint64_t nwrite);
/*
* nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled.
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c
index ab292ac8ae3..ab58ff5832b 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c
@@ -26,9 +26,9 @@
#include "nghttp3_vec.h"
#include "nghttp3_macro.h"
-size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
+uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
size_t i;
- size_t res = 0;
+ uint64_t res = 0;
for (i = 0; i < n; ++i) {
res += vec[i].len;
@@ -36,3 +36,20 @@ size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
return res;
}
+
+int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n) {
+ uint64_t res = 0;
+ size_t len;
+ size_t i;
+
+ for (i = 0; i < n; ++i) {
+ len = vec[i].len;
+ if (len > NGHTTP3_MAX_VARINT - res) {
+ return -1;
+ }
+
+ res += len;
+ }
+
+ return (int64_t)res;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h
index c1a928e3e1d..473d1467310 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h
@@ -32,4 +32,10 @@
#include <nghttp3/nghttp3.h>
+/*
+ * nghttp3_vec_len_varint is similar to nghttp3_vec_len, but it
+ * returns -1 if the sum of the length exceeds NGHTTP3_MAX_VARINT.
+ */
+int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n);
+
#endif /* NGHTTP3_VEC_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c
index dfad4793c4b..c460cc72835 100644
--- a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c
@@ -31,7 +31,7 @@
static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM,
NGHTTP3_VERSION};
-nghttp3_info *nghttp3_version(int least_version) {
+const nghttp3_info *nghttp3_version(int least_version) {
if (least_version > NGHTTP3_VERSION_NUM) {
return NULL;
}
diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp
index 9ea93be2091..a47a791610e 100644
--- a/deps/ngtcp2/ngtcp2.gyp
+++ b/deps/ngtcp2/ngtcp2.gyp
@@ -6,6 +6,9 @@
'ngtcp2_sources': [
'ngtcp2/lib/ngtcp2_acktr.c',
'ngtcp2/lib/ngtcp2_addr.c',
+ 'ngtcp2/lib/ngtcp2_balloc.c',
+ 'ngtcp2/lib/ngtcp2_bbr.c',
+ 'ngtcp2/lib/ngtcp2_bbr2.c',
'ngtcp2/lib/ngtcp2_buf.c',
'ngtcp2/lib/ngtcp2_cc.c',
'ngtcp2/lib/ngtcp2_cid.c',
@@ -19,8 +22,11 @@
'ngtcp2/lib/ngtcp2_log.c',
'ngtcp2/lib/ngtcp2_map.c',
'ngtcp2/lib/ngtcp2_mem.c',
+ 'ngtcp2/lib/ngtcp2_objalloc.c',
+ 'ngtcp2/lib/ngtcp2_opl.c',
'ngtcp2/lib/ngtcp2_path.c',
'ngtcp2/lib/ngtcp2_pkt.c',
+ 'ngtcp2/lib/ngtcp2_pmtud.c',
'ngtcp2/lib/ngtcp2_ppe.c',
'ngtcp2/lib/ngtcp2_pq.c',
'ngtcp2/lib/ngtcp2_pv.c',
@@ -34,7 +40,8 @@
'ngtcp2/lib/ngtcp2_strm.c',
'ngtcp2/lib/ngtcp2_vec.c',
'ngtcp2/lib/ngtcp2_version.c',
- 'ngtcp2/crypto/shared.c',
+ 'ngtcp2/lib/ngtcp2_window_filter.c',
+ 'ngtcp2/crypto/shared.c'
],
'ngtcp2_sources_openssl': [
'ngtcp2/crypto/openssl/openssl.c'
@@ -43,6 +50,7 @@
'ngtcp2/crypto/boringssl/boringssl.c'
],
'nghttp3_sources': [
+ 'nghttp3/lib/nghttp3_balloc.c',
'nghttp3/lib/nghttp3_buf.c',
'nghttp3/lib/nghttp3_conn.c',
'nghttp3/lib/nghttp3_conv.c',
@@ -55,6 +63,8 @@
'nghttp3/lib/nghttp3_ksl.c',
'nghttp3/lib/nghttp3_map.c',
'nghttp3/lib/nghttp3_mem.c',
+ 'nghttp3/lib/nghttp3_objalloc.c',
+ 'nghttp3/lib/nghttp3_opl.c',
'nghttp3/lib/nghttp3_pq.c',
'nghttp3/lib/nghttp3_qpack.c',
'nghttp3/lib/nghttp3_qpack_huffman.c',
@@ -117,6 +127,7 @@
'',
'ngtcp2/lib/includes',
'ngtcp2/crypto/includes',
+ 'ngtcp2/crypto',
]
},
'sources': [
diff --git a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c
index 8f75b485e3e..015032d41ca 100644
--- a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c
+++ b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c
@@ -35,15 +35,15 @@
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
+#include <openssl/aes.h>
#include <openssl/chacha.h>
+#include <openssl/rand.h>
#include "shared.h"
-/* Define cipher types because BoringSSL does not implement EVP
- interface for chacha20. */
typedef enum ngtcp2_crypto_boringssl_cipher_type {
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR,
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR,
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128,
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256,
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20,
} ngtcp2_crypto_boringssl_cipher_type;
@@ -51,22 +51,31 @@ typedef struct ngtcp2_crypto_boringssl_cipher {
ngtcp2_crypto_boringssl_cipher_type type;
} ngtcp2_crypto_boringssl_cipher;
-static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_128_ctr = {
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR,
+static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_128 = {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128,
};
-static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_256_ctr = {
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR,
+static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_256 = {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256,
};
static ngtcp2_crypto_boringssl_cipher crypto_cipher_chacha20 = {
NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20,
};
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm());
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)EVP_sha256();
+ return md;
+}
+
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aead_aes_128_gcm());
ctx->md.native_handle = (void *)EVP_sha256();
- ctx->hp.native_handle = (void *)&crypto_cipher_evp_aes_128_ctr;
+ ctx->hp.native_handle = (void *)&crypto_cipher_aes_128;
ctx->max_encryption = 0;
ctx->max_decryption_failure = 0;
return ctx;
@@ -123,9 +132,9 @@ static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) {
static const ngtcp2_crypto_boringssl_cipher *crypto_ssl_get_hp(SSL *ssl) {
switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
case TLS1_CK_AES_128_GCM_SHA256:
- return &crypto_cipher_evp_aes_128_ctr;
+ return &crypto_cipher_aes_128;
case TLS1_CK_AES_256_GCM_SHA384:
- return &crypto_cipher_evp_aes_256_ctr;
+ return &crypto_cipher_aes_256;
case TLS1_CK_CHACHA20_POLY1305_SHA256:
return &crypto_cipher_chacha20;
default:
@@ -216,19 +225,15 @@ void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
}
}
-typedef enum ngtcp2_crypto_boringssl_cipher_ctx_type {
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP,
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20,
-} ngtcp2_crypto_boringssl_cipher_ctx_type;
-
typedef struct ngtcp2_crypto_boringssl_cipher_ctx {
- ngtcp2_crypto_boringssl_cipher_ctx_type type;
+ ngtcp2_crypto_boringssl_cipher_type type;
union {
- /* ctx is EVP_CIPHER_CTX used when type ==
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP. */
- EVP_CIPHER_CTX *ctx;
+ /* aes_key is an encryption key when type is either
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128 or
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256. */
+ AES_KEY aes_key;
/* key contains an encryption key when type ==
- NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20. */
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20. */
uint8_t key[32];
};
} ngtcp2_crypto_boringssl_cipher_ctx;
@@ -238,70 +243,41 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
const uint8_t *key) {
ngtcp2_crypto_boringssl_cipher *hp_cipher = cipher->native_handle;
ngtcp2_crypto_boringssl_cipher_ctx *ctx;
- EVP_CIPHER_CTX *actx;
- const EVP_CIPHER *evp_cipher;
-
- switch (hp_cipher->type) {
- case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR:
- evp_cipher = EVP_aes_128_ctr();
- break;
- case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR:
- evp_cipher = EVP_aes_256_ctr();
- break;
- case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
- ctx = malloc(sizeof(*ctx));
- if (ctx == NULL) {
- return -1;
- }
-
- ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20;
- memcpy(ctx->key, key, sizeof(ctx->key));
-
- cipher_ctx->native_handle = ctx;
-
- return 0;
- default:
- assert(0);
- };
-
- actx = EVP_CIPHER_CTX_new();
- if (actx == NULL) {
- return -1;
- }
-
- if (!EVP_EncryptInit_ex(actx, evp_cipher, NULL, key, NULL)) {
- EVP_CIPHER_CTX_free(actx);
- return -1;
- }
+ int rv;
+ (void)rv;
ctx = malloc(sizeof(*ctx));
if (ctx == NULL) {
- EVP_CIPHER_CTX_free(actx);
return -1;
}
- ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP;
- ctx->ctx = actx;
-
+ ctx->type = hp_cipher->type;
cipher_ctx->native_handle = ctx;
- return 0;
+ switch (hp_cipher->type) {
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128:
+ rv = AES_set_encrypt_key(key, 128, &ctx->aes_key);
+ assert(0 == rv);
+ return 0;
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256:
+ rv = AES_set_encrypt_key(key, 256, &ctx->aes_key);
+ assert(0 == rv);
+ return 0;
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
+ memcpy(ctx->key, key, sizeof(ctx->key));
+ return 0;
+ default:
+ assert(0);
+ abort();
+ };
}
void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
- ngtcp2_crypto_boringssl_cipher_ctx *ctx;
-
if (!cipher_ctx->native_handle) {
return;
}
- ctx = cipher_ctx->native_handle;
-
- if (ctx->type == NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP) {
- EVP_CIPHER_CTX_free(ctx->ctx);
- }
-
- free(ctx);
+ free(cipher_ctx->native_handle);
}
int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
@@ -331,18 +307,32 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
return 0;
}
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ const EVP_MD *prf = md->native_handle;
+
+ if (HKDF(dest, destlen, prf, secret, secretlen, salt, saltlen, info,
+ infolen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *plaintext, size_t plaintextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen) {
+ const uint8_t *aad, size_t aadlen) {
const EVP_AEAD *cipher = aead->native_handle;
EVP_AEAD_CTX *actx = aead_ctx->native_handle;
size_t max_outlen = plaintextlen + EVP_AEAD_max_overhead(cipher);
size_t outlen;
if (EVP_AEAD_CTX_seal(actx, dest, &outlen, max_outlen, nonce, noncelen,
- plaintext, plaintextlen, ad, adlen) != 1) {
+ plaintext, plaintextlen, aad, aadlen) != 1) {
return -1;
}
@@ -353,15 +343,21 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *ciphertext, size_t ciphertextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen) {
+ const uint8_t *aad, size_t aadlen) {
+ const EVP_AEAD *cipher = aead->native_handle;
EVP_AEAD_CTX *actx = aead_ctx->native_handle;
- size_t max_outlen = ciphertextlen;
+ size_t max_overhead = EVP_AEAD_max_overhead(cipher);
+ size_t max_outlen;
size_t outlen;
- (void)aead;
+ if (ciphertextlen < max_overhead) {
+ return -1;
+ }
+
+ max_outlen = ciphertextlen - max_overhead;
if (EVP_AEAD_CTX_open(actx, dest, &outlen, max_outlen, nonce, noncelen,
- ciphertext, ciphertextlen, ad, adlen) != 1) {
+ ciphertext, ciphertextlen, aad, aadlen) != 1) {
return -1;
}
@@ -373,23 +369,16 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
const uint8_t *sample) {
static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
ngtcp2_crypto_boringssl_cipher_ctx *ctx = hp_ctx->native_handle;
- EVP_CIPHER_CTX *actx;
- int len;
uint32_t counter;
(void)hp;
switch (ctx->type) {
- case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP:
- actx = ctx->ctx;
- if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) ||
- !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT,
- sizeof(PLAINTEXT) - 1) ||
- !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) {
- return -1;
- }
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128:
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256:
+ AES_ecb_encrypt(sample, dest, &ctx->aes_key, 1);
return 0;
- case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20:
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
#if defined(WORDS_BIGENDIAN)
counter = (uint32_t)sample[0] + (uint32_t)(sample[1] << 8) +
(uint32_t)(sample[2] << 16) + (uint32_t)(sample[3] << 24);
@@ -434,10 +423,7 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
SSL_reset_early_data_reject(ssl);
- rv = ngtcp2_conn_early_data_rejected(conn);
- if (rv != 0) {
- return -1;
- }
+ ngtcp2_conn_early_data_rejected(conn);
goto retry;
default:
@@ -472,24 +458,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
SSL *ssl = tls;
- ngtcp2_transport_params_type exttype =
- ngtcp2_conn_is_server(conn)
- ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
- : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS;
const uint8_t *tp;
size_t tplen;
- ngtcp2_transport_params params;
int rv;
SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
- rv = ngtcp2_decode_transport_params(&params, exttype, tp, tplen);
- if (rv != 0) {
- ngtcp2_conn_set_tls_error(conn, rv);
- return -1;
- }
-
- rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
if (rv != 0) {
ngtcp2_conn_set_tls_error(conn, rv);
return -1;
@@ -520,6 +495,7 @@ ngtcp2_crypto_level ngtcp2_crypto_boringssl_from_ssl_encryption_level(
return NGTCP2_CRYPTO_LEVEL_APPLICATION;
default:
assert(0);
+ abort();
}
}
@@ -536,5 +512,116 @@ enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
return ssl_encryption_early_data;
default:
assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ if (RAND_bytes(data, datalen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_read_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ const SSL_CIPHER *cipher, const uint8_t *secret,
+ size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
+ (void)cipher;
+
+ if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int set_write_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ const SSL_CIPHER *cipher, const uint8_t *secret,
+ size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
+ (void)cipher;
+
+ if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return 0;
}
+
+ return 1;
+}
+
+static int add_handshake_data(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
+ int rv;
+
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int flush_flight(SSL *ssl) {
+ (void)ssl;
+ return 1;
+}
+
+static int send_alert(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ uint8_t alert) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)bssl_level;
+
+ ngtcp2_conn_set_tls_alert(conn, alert);
+
+ return 1;
+}
+
+static SSL_QUIC_METHOD quic_method = {
+ set_read_secret, set_write_secret, add_handshake_data,
+ flush_flight, send_alert,
+};
+
+static void crypto_boringssl_configure_context(SSL_CTX *ssl_ctx) {
+ SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+}
+
+int ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx) {
+ crypto_boringssl_configure_context(ssl_ctx);
+
+ return 0;
+}
+
+int ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx) {
+ crypto_boringssl_configure_context(ssl_ctx);
+
+ return 0;
}
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
index 23901d18c16..4736b51c3cb 100644
--- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
@@ -31,6 +31,13 @@
extern "C" {
#endif
+#ifdef WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <ws2tcpip.h>
+#endif /* WIN32 */
+
/**
* @macro
*
@@ -58,19 +65,10 @@ extern "C" {
/**
* @function
*
- * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
- * encryption and decryption.
- */
-NGTCP2_EXTERN ngtcp2_crypto_ctx *
-ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
-
-/**
- * @function
- *
* `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated
* ciphers and message digests from native TLS session
* |tls_native_handle|. This is used for encrypting/decrypting
- * Handshake and Short packets.
+ * Handshake and Short header packets.
*
* If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
* a pointer to SSL object.
@@ -95,33 +93,6 @@ ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle);
/**
* @function
*
- * `ngtcp2_crypto_aead_init` initializes |aead| with the provided
- * |aead_native_handle| which is an underlying AEAD object.
- *
- * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
- * a pointer to EVP_CIPHER.
- *
- * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
- * gnutls_cipher_algorithm_t casted to ``void *``.
- *
- * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
- * be a pointer to EVP_AEAD.
- */
-NGTCP2_EXTERN ngtcp2_crypto_aead *
-ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, void *aead_native_handle);
-
-/**
- * @function
- *
- * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
- * AEAD_AES_128_GCM for Retry packet integrity protection.
- */
-NGTCP2_EXTERN ngtcp2_crypto_aead *
-ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
-
-/**
- * @function
- *
* `ngtcp2_crypto_md_init` initializes |md| with the provided
* |md_native_handle| which is an underlying message digest object.
*
@@ -194,6 +165,20 @@ NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
/**
* @function
*
+ * `ngtcp2_crypto_hkdf` performs HKDF operation. The result is
+ * |destlen| bytes long and is stored to the buffer pointed by |dest|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen);
+
+/**
+ * @function
+ *
* `ngtcp2_crypto_hkdf_expand_label` performs HKDF expand label. The
* result is |destlen| bytes long and is stored to the buffer pointed
* by |dest|.
@@ -238,36 +223,13 @@ ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead);
/**
* @function
*
- * `ngtcp2_crypto_derive_packet_protection_key` derives packet
- * protection key. This function writes packet protection key into
- * the buffer pointed by |key|. |key| must point to the buffer which
- * is at least ngtcp2_crypto_aead_keylen(aead) bytes long. This
- * function writes packet protection IV into |iv|. |iv| must point to
- * the buffer which is at least
- * ngtcp2_crypto_packet_protection_ivlen(aead). |key| is
- * ngtcp2_crypto_aead_keylen(aead) bytes long. |iv| is
- * ngtcp2_crypto_packet_protection_ivlen(aead) bytes long.
- *
- * If |hp| is not NULL, this function also derives packet header
- * protection key and writes the key into the buffer pointed by |hp|.
- * The length of key is ngtcp2_crypto_aead_keylen(aead) bytes long.
- * |hp|, if not NULL, must have enough capacity to store the key.
- *
- * This function returns 0 if it succeeds, or -1.
- */
-NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key(
- uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead,
- const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen);
-
-/**
- * @function
- *
* `ngtcp2_crypto_encrypt` encrypts |plaintext| of length
* |plaintextlen| and writes the ciphertext into the buffer pointed by
* |dest|. The length of ciphertext is plaintextlen +
- * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have
- * enough capacity to store the ciphertext. It is allowed to specify
- * the same value to |dest| and |plaintext|.
+ * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
+ * bytes long. |dest| must have enough capacity to store the
+ * ciphertext. It is allowed to specify the same value to |dest| and
+ * |plaintext|.
*
* This function returns 0 if it succeeds, or -1.
*/
@@ -277,14 +239,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest,
const uint8_t *plaintext,
size_t plaintextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen);
+ const uint8_t *aad, size_t aadlen);
/**
* @function
*
* `ngtcp2_crypto_encrypt_cb` is a wrapper function around
* `ngtcp2_crypto_encrypt`. It can be directly passed to
- * :member:`ngtcp2_conn_callbacks.encrypt` field.
+ * :member:`ngtcp2_callbacks.encrypt` field.
*
* This function returns 0 if it succeeds, or
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
@@ -294,7 +256,7 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *plaintext, size_t plaintextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen);
+ const uint8_t *aad, size_t aadlen);
/**
* @function
@@ -302,9 +264,10 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
* `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length
* |ciphertextlen| and writes the plaintext into the buffer pointed by
* |dest|. The length of plaintext is ciphertextlen -
- * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have
- * enough capacity to store the plaintext. It is allowed to specify
- * the same value to |dest| and |ciphertext|.
+ * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
+ * bytes long. |dest| must have enough capacity to store the
+ * plaintext. It is allowed to specify the same value to |dest| and
+ * |ciphertext|.
*
* This function returns 0 if it succeeds, or -1.
*/
@@ -314,14 +277,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest,
const uint8_t *ciphertext,
size_t ciphertextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen);
+ const uint8_t *aad, size_t aadlen);
/**
* @function
*
* `ngtcp2_crypto_decrypt_cb` is a wrapper function around
* `ngtcp2_crypto_decrypt`. It can be directly passed to
- * :member:`ngtcp2_conn_callbacks.decrypt` field.
+ * :member:`ngtcp2_callbacks.decrypt` field.
*
* This function returns 0 if it succeeds, or
* :macro:`NGTCP2_ERR_TLS_DECRYPT`.
@@ -331,15 +294,19 @@ ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *ciphertext, size_t ciphertextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen);
+ const uint8_t *aad, size_t aadlen);
/**
* @function
*
* `ngtcp2_crypto_hp_mask` generates mask which is used in packet
* header encryption. The mask is written to the buffer pointed by
- * |dest|. The length of mask is 5 bytes. |dest| must have enough
- * capacity to store the mask.
+ * |dest|. The sample is passed as |sample| which is
+ * :macro:`NGTCP2_HP_SAMPLELEN` bytes long. The length of mask must
+ * be at least :macro:`NGTCP2_HP_MASKLEN`. The library only uses the
+ * first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced mask. The
+ * buffer pointed by |dest| must have at least
+ * :macro:`NGTCP2_HP_SAMPLELEN` bytes available.
*
* This function returns 0 if it succeeds, or -1.
*/
@@ -353,7 +320,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest,
*
* `ngtcp2_crypto_hp_mask_cb` is a wrapper function around
* `ngtcp2_crypto_hp_mask`. It can be directly passed to
- * :member:`ngtcp2_conn_callbacks.hp_mask` field.
+ * :member:`ngtcp2_callbacks.hp_mask` field.
*
* This function returns 0 if it succeeds, or
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
@@ -379,16 +346,30 @@ ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
* |secretlen| specifies the length of |secret|.
*
* The length of packet protection key and header protection key is
- * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection
- * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx
- * can be obtained by `ngtcp2_crypto_ctx_tls`.
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
+ * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
*
* In the first call of this function, it calls
- * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message
- * digest algorithm. After the successful call of this function,
- * application can use `ngtcp2_conn_get_crypto_ctx` to get the object.
- * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag
- * length.
+ * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
+ * negotiated AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
+ * :type:`ngtcp2_crypto_ctx`.
+ *
+ * If |conn| is initialized as client, and |level| is
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
+ * function retrieves a remote QUIC transport parameters extension
+ * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
+ * sets it to |conn| by calling
+ * `ngtcp2_conn_decode_remote_transport_params`.
*
* This function returns 0 if it succeeds, or -1.
*/
@@ -412,20 +393,30 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key(
* |secretlen| specifies the length of |secret|.
*
* The length of packet protection key and header protection key is
- * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection
- * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx
- * can be obtained by `ngtcp2_crypto_ctx_tls`.
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
+ * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
*
* In the first call of this function, it calls
- * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message
- * digest algorithm. After the successful call of this function,
- * application can use `ngtcp2_conn_get_crypto_ctx` to get the object.
- * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag
- * length.
- *
- * If |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a
- * remote QUIC transport parameters extension from |tls| and sets it
- * to |conn|.
+ * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
+ * negotiated AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
+ * :type:`ngtcp2_crypto_ctx`.
+ *
+ * If |conn| is initialized as server, and |level| is
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
+ * function retrieves a remote QUIC transport parameters extension
+ * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
+ * sets it to |conn| by calling
+ * `ngtcp2_conn_decode_remote_transport_params`.
*
* This function returns 0 if it succeeds, or -1.
*/
@@ -461,9 +452,11 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key(
* length of |rx_secret| and |tx_secret|.
*
* The length of packet protection key and header protection key is
- * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection
- * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx
- * can be obtained by `ngtcp2_conn_get_crypto_ctx`.
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls`.
*
* This function returns 0 if it succeeds, or -1.
*/
@@ -479,7 +472,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key(
*
* `ngtcp2_crypto_update_key_cb` is a wrapper function around
* `ngtcp2_crypto_update_key`. It can be directly passed to
- * :member:`ngtcp2_conn_callbacks.update_key` field.
+ * :member:`ngtcp2_callbacks.update_key` field.
*
* This function returns 0 if it succeeds, or
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
@@ -498,8 +491,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb(
* encryption keys and sets QUIC transport parameters.
*
* This function can be directly passed to
- * :member:`ngtcp2_conn_callbacks.client_initial` field. It is only
- * used by client.
+ * :member:`ngtcp2_callbacks.client_initial` field. It is only used
+ * by client.
*
* This function returns 0 if it succeeds, or
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
@@ -514,7 +507,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn,
* response to incoming Retry packet.
*
* This function can be directly passed to
- * :member:`ngtcp2_conn_callbacks.recv_retry` field. It is only used
+ * :member:`ngtcp2_callbacks.recv_retry` field. It is only used
* by client.
*
* This function returns 0 if it succeeds, or
@@ -532,7 +525,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn,
* transport parameters.
*
* This function can be directly passed to
- * :member:`ngtcp2_conn_callbacks.recv_client_initial` field. It is
+ * :member:`ngtcp2_callbacks.recv_client_initial` field. It is
* only used by server.
*
* This function returns 0 if it succeeds, or
@@ -549,8 +542,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
* length |datalen| in encryption level |crypto_level| and may feed
* outgoing CRYPTO data to |conn|. This function can drive handshake.
* This function can be also used after handshake completes. It is
- * allowed to call this function with datalen == 0. In this case, no
- * additional read operation is done.
+ * allowed to call this function with |datalen| == 0. In this case,
+ * no additional read operation is done.
*
* This function returns 0 if it succeeds, or a negative error code.
* The generic error code is -1 if a specific error code is not
@@ -566,31 +559,182 @@ ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
/**
* @function
*
+ * `ngtcp2_crypto_recv_crypto_data_cb` is a wrapper function around
+ * `ngtcp2_crypto_read_write_crypto_data`. It can be directly passed
+ * to :member:`ngtcp2_callbacks.recv_crypto_data` field.
+ *
+ * If this function is used, the TLS implementation specific error
+ * codes described in `ngtcp2_crypto_read_write_crypto_data` are
+ * treated as if it returns -1. Do not use this function if an
+ * application wishes to use the TLS implementation specific error
+ * codes.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, uint64_t offset,
+ const uint8_t *data, size_t datalen, void *user_data);
+
+/**
+ * @function
+ *
* `ngtcp2_crypto_generate_stateless_reset_token` generates a
- * stateless reset token using HKDF extraction with |md| using the
- * given |cid| and static key |secret| as input. The token will be
- * written to the buffer pointed by |token| and it must have a
- * capacity of at least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN`
- * bytes.
+ * stateless reset token using HKDF extraction using the given |cid|
+ * and static key |secret| as input. The token will be written to
+ * the buffer pointed by |token| and it must have a capacity of at
+ * least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` bytes.
*
* This function returns 0 if it succeeds, or -1.
*/
NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
- uint8_t *token, const ngtcp2_crypto_md *md, const uint8_t *secret,
- size_t secretlen, const ngtcp2_cid *cid);
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_cid *cid);
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_RAND_DATALEN` is the length of random
+ * data added to a token generated by
+ * `ngtcp2_crypto_generate_retry_token` or
+ * `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_RAND_DATALEN 32
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for
+ * Retry token generated by `ngtcp2_crypto_generate_retry_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR` is the magic byte for a
+ * token generated by `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR 0x36
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` is the maximum length of
+ * a token generated by `ngtcp2_crypto_generate_retry_token`.
+ */
+#define NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN \
+ (/* magic = */ 1 + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN + \
+ sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length
+ * of a token generated by `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \
+ (/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_retry_token` generates a token in the
+ * buffer pointed by |token| that is sent with Retry packet. The
+ * buffer pointed by |token| must have at least
+ * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` bytes long. The
+ * successfully generated token starts with
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`. |secret| of length
+ * |secretlen| is an initial keying material to generate keys to
+ * encrypt the token. |version| is QUIC version. |remote_addr| of
+ * length |remote_addrlen| is an address of client. |retry_scid| is a
+ * Source Connection ID chosen by server and set in Retry packet.
+ * |odcid| is a Destination Connection ID in Initial packet sent by
+ * client. |ts| is the timestamp when the token is generated.
+ *
+ * This function returns the length of generated token if it succeeds,
+ * or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in
+ * the buffer pointed by |token| of length |tokenlen|. |secret| of
+ * length |secretlen| is an initial keying material to generate keys
+ * to decrypt the token. |version| is QUIC version of the Initial
+ * packet that contains this token. |remote_addr| of length
+ * |remote_addrlen| is an address of client. |dcid| is a Destination
+ * Connection ID in Initial packet sent by client. |timeout| is the
+ * period during which the token is valid. |ts| is the current
+ * timestamp. When validation succeeds, the extracted Destination
+ * Connection ID (which is the Destination Connection ID in Initial
+ * packet sent by client that triggered Retry packet) is stored to the
+ * buffer pointed by |odcid|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token(
+ ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_regular_token` generates a token in the
+ * buffer pointed by |token| that is sent with NEW_TOKEN frame. The
+ * buffer pointed by |token| must have at least
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes long. The
+ * successfully generated token starts with
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length
+ * |secretlen| is an initial keying material to generate keys to
+ * encrypt the token. |remote_addr| of length |remote_addrlen| is an
+ * address of client. |ts| is the timestamp when the token is
+ * generated.
+ *
+ * This function returns the length of generated token if it succeeds,
+ * or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_verify_regular_token` verifies a regular token
+ * stored in the buffer pointed by |token| of length |tokenlen|.
+ * |secret| of length |secretlen| is an initial keying material to
+ * generate keys to decrypt the token. |remote_addr| of length
+ * |remote_addrlen| is an address of client. |timeout| is the period
+ * during which the token is valid. |ts| is the current timestamp.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token(
+ const uint8_t *token, size_t tokenlen, const uint8_t *secret,
+ size_t secretlen, const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
/**
* @function
*
* `ngtcp2_crypto_write_connection_close` writes Initial packet
- * containing CONNECTION_CLOSE with the given |error_code| to the
- * buffer pointed by |dest| of length |destlen|. This function is
- * designed for server to close connection without committing the
- * state when validating Retry token fails. This function must not be
- * used by client. The |dcid| must be the Source Connection ID in
- * Initial packet from client. The |scid| must be the Destination
- * Connection ID in Initial packet from client. |scid| is used to
- * derive initial keying materials.
+ * containing CONNECTION_CLOSE with the given |error_code| and the
+ * optional |reason| of length |reasonlen| to the buffer pointed by
+ * |dest| of length |destlen|. This function is designed for server
+ * to close connection without committing the state when validating
+ * Retry token fails. This function must not be used by client. The
+ * |dcid| must be the Source Connection ID in Initial packet from
+ * client. The |scid| must be the Destination Connection ID in
+ * Initial packet from client. |scid| is used to derive initial
+ * keying materials.
*
* This function wraps around `ngtcp2_pkt_write_connection_close` for
* easier use.
@@ -599,7 +743,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close(
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid, uint64_t error_code);
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen);
/**
* @function
@@ -664,7 +809,7 @@ ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx);
* `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|.
*
* This function can be directly passed to
- * :member:`ngtcp2_conn_callbacks.delete_crypto_aead_ctx` field.
+ * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` field.
*/
NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb(
ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data);
@@ -676,11 +821,71 @@ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb(
* |cipher_ctx|.
*
* This function can be directly passed to
- * :member:`ngtcp2_conn_callbacks.delete_crypto_cipher_ctx` field.
+ * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` field.
*/
NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data);
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_get_path_challenge_data_cb` writes unpredictable
+ * sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data|
+ * which is sent with PATH_CHALLENGE frame.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.get_path_challenge_data` field.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn,
+ uint8_t *data,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for
+ * |version| which is negotiated or being negotiated. |client_dcid|
+ * is the destination connection ID in first Initial packet of client.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.version_negotiation` field.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data);
+
+typedef struct ngtcp2_crypto_conn_ref ngtcp2_crypto_conn_ref;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_crypto_get_conn` is a callback function to get a
+ * pointer to :type:`ngtcp2_conn` from |conn_ref|. The implementation
+ * must return non-NULL :type:`ngtcp2_conn` object.
+ */
+typedef ngtcp2_conn *(*ngtcp2_crypto_get_conn)(
+ ngtcp2_crypto_conn_ref *conn_ref);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_conn_ref` is a structure to get a pointer to
+ * :type:`ngtcp2_conn`. It is meant to be set to TLS native handle as
+ * an application specific data (e.g. SSL_set_app_data in OpenSSL).
+ */
+typedef struct ngtcp2_crypto_conn_ref {
+ /**
+ * :member:`get_conn` is a callback function to get a pointer to
+ * :type:`ngtcp2_conn` object.
+ */
+ ngtcp2_crypto_get_conn get_conn;
+ /**
+ * :member:`user_data` is a pointer to arbitrary user data.
+ */
+ void *user_data;
+} ngtcp2_crypto_conn_ref;
+
#ifdef __cplusplus
}
#endif
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h
index 65f8414249a..6497c09e798 100644
--- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h
@@ -55,6 +55,48 @@ NGTCP2_EXTERN enum ssl_encryption_level_t
ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
ngtcp2_crypto_level crypto_level);
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_configure_server_context` configures
+ * |ssl_ctx| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_configure_client_context` configures
+ * |ssl_ctx| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h
index 10a8e712cfd..844081bfa8b 100644
--- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h
@@ -83,6 +83,48 @@ NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL
ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
ngtcp2_crypto_level crypto_level);
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_configure_server_context` configures
+ * |ssl_ctx| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_configure_client_context` configures
+ * |ssl_ctx| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h
new file mode 100644
index 00000000000..d4b551c382f
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h
@@ -0,0 +1,246 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_PICOTLS_H
+#define NGTCP2_CRYPTO_PICOTLS_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <picotls.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_picotls_ctx` contains per-connection state
+ * of Picotls objects and must be an object to bet set to
+ * `ngtcp2_conn_set_tls_native_handle`.
+ */
+typedef struct ngtcp2_crypto_picotls_ctx {
+ /**
+ * :member:`ptls` is a pointer to ptls_t object.
+ */
+ ptls_t *ptls;
+ /**
+ * :member:`handshake_properties` is a set of configurations used
+ * during this particular TLS handshake.
+ */
+ ptls_handshake_properties_t handshake_properties;
+} ngtcp2_crypto_picotls_ctx;
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_ctx_init` initializes the object pointed by
+ * |cptls|. |cptls| must not be NULL.
+ */
+NGTCP2_EXTERN void
+ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_from_epoch` translates |epoch| to
+ * :type:`ngtcp2_crypto_level`. This function is only available for
+ * Picotls backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_picotls_from_epoch(size_t epoch);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to epoch. This function is only available for
+ * Picotls backend.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_server_context` configures |ctx|
+ * for server side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set max_early_data_size to UINT32_MAX.
+ * - Set omit_end_of_early_data to 1.
+ * - Set update_traffic_key callback.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_client_context` configures |ctx|
+ * for client side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set omit_end_of_early_data to 1.
+ * - Set update_traffic_key callback.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_server_session` configures |cptls|
+ * for server side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set handshake_properties.collect_extension to
+ * `ngtcp2_crypto_picotls_collect_extension`.
+ * - Set handshake_properties.collected_extensions to
+ * `ngtcp2_crypto_picotls_collected_extensions`.
+ *
+ * The callbacks set by this function only handle QUIC Transport
+ * Parameters TLS extension. If an application needs to handle the
+ * other TLS extensions, set its own callbacks and call
+ * `ngtcp2_crypto_picotls_collect_extension` and
+ * `ngtcp2_crypto_picotls_collected_extensions` form them.
+ *
+ * During the QUIC handshake, the first element of
+ * handshake_properties.additional_extensions is assigned to send QUIC
+ * Transport Parameter TLS extension. Therefore, an application must
+ * allocate at least 2 elements for
+ * handshake_properties.additional_extensions.
+ *
+ * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the
+ * resources.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_picotls_configure_server_session(
+ ngtcp2_crypto_picotls_ctx *cptls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_client_session` configures |cptls|
+ * for client side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set handshake_properties.max_early_data_size to a pointer to
+ * uint32_t, which is allocated dynamically by this function.
+ * - Set handshake_properties.collect_extension to
+ * `ngtcp2_crypto_picotls_collect_extension`.
+ * - Set handshake_properties.collected_extensions to
+ * `ngtcp2_crypto_picotls_collected_extensions`.
+ * - Set handshake_properties.additional_extensions[0].data to the
+ * dynamically allocated buffer which contains QUIC Transport
+ * Parameters TLS extension. An application must allocate at least
+ * 2 elements for handshake_properties.additional_extensions.
+ *
+ * The callbacks set by this function only handle QUIC Transport
+ * Parameters TLS extension. If an application needs to handle the
+ * other TLS extensions, set its own callbacks and call
+ * `ngtcp2_crypto_picotls_collect_extension` and
+ * `ngtcp2_crypto_picotls_collected_extensions` form them.
+ *
+ * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the
+ * resources.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_picotls_configure_client_session(ngtcp2_crypto_picotls_ctx *cptls,
+ ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_deconfigure_session` frees the resources
+ * allocated for |cptls| during QUIC connection. It frees the
+ * following data using :manpage:`free(3)`.
+ *
+ * - handshake_properties.max_early_data_size
+ * - handshake_properties.additional_extensions[0].data.base
+ *
+ * If |cptls| is NULL, this function does nothing.
+ */
+NGTCP2_EXTERN void
+ngtcp2_crypto_picotls_deconfigure_session(ngtcp2_crypto_picotls_ctx *cptls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_collect_extension` is a callback function
+ * which only returns nonzero if |type| ==
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_picotls_collect_extension(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ uint16_t type);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_collected_extensions` is a callback function
+ * which only handles the extension of type
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`. The other
+ * extensions are ignored.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_picotls_collected_extensions(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ ptls_raw_extension_t *extensions);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_PICOTLS_H */
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h
new file mode 100644
index 00000000000..3b10802c25b
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h
@@ -0,0 +1,106 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_WOLFSSL_H
+#define NGTCP2_CRYPTO_WOLFSSL_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level` translates
+ * |wolfssl_level| to :type:`ngtcp2_crypto_level`. This function is only
+ * available for wolfSSL backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to WOLFSSL_ENCRYPTION_LEVEL. This function is only
+ * available for wolfSSL backend.
+ */
+NGTCP2_EXTERN WOLFSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_configure_server_context` configures
+ * |ssl_ctx| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * WOLFSSL object by calling wolfSSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_configure_client_context` configures
+ * |ssl_ctx| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling wolfSSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_WOLFSSL_H */
diff --git a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c
index d4b9e57f275..466d9e11ca6 100644
--- a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c
+++ b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c
@@ -34,6 +34,11 @@
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
+#include <openssl/rand.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+# include <openssl/core_names.h>
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
#include "shared.h"
@@ -48,9 +53,19 @@ static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) {
return EVP_CCM_TLS_TAG_LEN;
default:
assert(0);
+ abort(); /* if NDEBUG is set */
}
}
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm());
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)EVP_sha256();
+ return md;
+}
+
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aes_128_gcm());
ctx->md.native_handle = (void *)EVP_sha256();
@@ -187,18 +202,37 @@ int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
const EVP_CIPHER *cipher = aead->native_handle;
int cipher_nid = EVP_CIPHER_nid(cipher);
EVP_CIPHER_CTX *actx;
+ size_t taglen = crypto_aead_max_overhead(cipher);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[3];
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
actx = EVP_CIPHER_CTX_new();
if (actx == NULL) {
return -1;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen);
+
+ if (cipher_nid == NID_aes_128_ccm) {
+ params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ NULL, taglen);
+ params[2] = OSSL_PARAM_construct_end();
+ } else {
+ params[1] = OSSL_PARAM_construct_end();
+ }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_set_params(actx, params) ||
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen,
NULL) ||
(cipher_nid == NID_aes_128_ccm &&
- !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG,
- (int)crypto_aead_max_overhead(cipher), NULL)) ||
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) ||
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
!EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) {
EVP_CIPHER_CTX_free(actx);
return -1;
@@ -215,18 +249,37 @@ int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
const EVP_CIPHER *cipher = aead->native_handle;
int cipher_nid = EVP_CIPHER_nid(cipher);
EVP_CIPHER_CTX *actx;
+ size_t taglen = crypto_aead_max_overhead(cipher);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[3];
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
actx = EVP_CIPHER_CTX_new();
if (actx == NULL) {
return -1;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen);
+
+ if (cipher_nid == NID_aes_128_ccm) {
+ params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ NULL, taglen);
+ params[2] = OSSL_PARAM_construct_end();
+ } else {
+ params[1] = OSSL_PARAM_construct_end();
+ }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_set_params(actx, params) ||
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen,
NULL) ||
(cipher_nid == NID_aes_128_ccm &&
- !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG,
- (int)crypto_aead_max_overhead(cipher), NULL)) ||
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) ||
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
!EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) {
EVP_CIPHER_CTX_free(actx);
return -1;
@@ -272,6 +325,33 @@ void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
const uint8_t *secret, size_t secretlen,
const uint8_t *salt, size_t saltlen) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const EVP_MD *prf = md->native_handle;
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
+ EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
+ int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY;
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)EVP_MD_get0_name(prf), 0),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
+ secretlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt,
+ saltlen),
+ OSSL_PARAM_construct_end(),
+ };
+ int rv = 0;
+
+ EVP_KDF_free(kdf);
+
+ if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) {
+ rv = -1;
+ }
+
+ EVP_KDF_CTX_free(kctx);
+
+ return rv;
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
const EVP_MD *prf = md->native_handle;
int rv = 0;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
@@ -293,12 +373,40 @@ int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
EVP_PKEY_CTX_free(pctx);
return rv;
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
}
int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
const ngtcp2_crypto_md *md, const uint8_t *secret,
size_t secretlen, const uint8_t *info,
size_t infolen) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const EVP_MD *prf = md->native_handle;
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
+ EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
+ int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY;
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)EVP_MD_get0_name(prf), 0),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
+ secretlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info,
+ infolen),
+ OSSL_PARAM_construct_end(),
+ };
+ int rv = 0;
+
+ EVP_KDF_free(kdf);
+
+ if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
+ rv = -1;
+ }
+
+ EVP_KDF_CTX_free(kctx);
+
+ return rv;
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
const EVP_MD *prf = md->native_handle;
int rv = 0;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
@@ -309,7 +417,7 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
if (EVP_PKEY_derive_init(pctx) != 1 ||
EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 ||
EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 ||
- EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)"", 0) != 1 ||
EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 ||
EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 ||
EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
@@ -319,29 +427,97 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
EVP_PKEY_CTX_free(pctx);
return rv;
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const EVP_MD *prf = md->native_handle;
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
+ EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)EVP_MD_get0_name(prf), 0),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
+ secretlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt,
+ saltlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info,
+ infolen),
+ OSSL_PARAM_construct_end(),
+ };
+ int rv = 0;
+
+ EVP_KDF_free(kdf);
+
+ if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
+ rv = -1;
+ }
+
+ EVP_KDF_CTX_free(kctx);
+
+ return rv;
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ const EVP_MD *prf = md->native_handle;
+ int rv = 0;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL) {
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1 ||
+ EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) !=
+ 1 ||
+ EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 ||
+ EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 ||
+ EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
+ rv = -1;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+
+ return rv;
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
}
int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *plaintext, size_t plaintextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen) {
+ const uint8_t *aad, size_t aadlen) {
const EVP_CIPHER *cipher = aead->native_handle;
size_t taglen = crypto_aead_max_overhead(cipher);
int cipher_nid = EVP_CIPHER_nid(cipher);
EVP_CIPHER_CTX *actx = aead_ctx->native_handle;
int len;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ dest + plaintextlen, taglen),
+ OSSL_PARAM_construct_end(),
+ };
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
(void)noncelen;
if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) ||
(cipher_nid == NID_aes_128_ccm &&
!EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) ||
- !EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) ||
+ !EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) ||
!EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) ||
!EVP_EncryptFinal_ex(actx, dest + len, &len) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_get_params(actx, params)
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen,
- dest + plaintextlen)) {
+ dest + plaintextlen)
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ ) {
return -1;
}
@@ -352,13 +528,16 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *ciphertext, size_t ciphertextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen) {
+ const uint8_t *aad, size_t aadlen) {
const EVP_CIPHER *cipher = aead->native_handle;
size_t taglen = crypto_aead_max_overhead(cipher);
int cipher_nid = EVP_CIPHER_nid(cipher);
EVP_CIPHER_CTX *actx = aead_ctx->native_handle;
int len;
const uint8_t *tag;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[2];
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
(void)noncelen;
@@ -369,12 +548,22 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
ciphertextlen -= taglen;
tag = ciphertext + ciphertextlen;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ (void *)tag, taglen);
+ params[1] = OSSL_PARAM_construct_end();
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_set_params(actx, params) ||
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen,
(uint8_t *)tag) ||
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
(cipher_nid == NID_aes_128_ccm &&
!EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) ||
- !EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) ||
+ !EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) ||
!EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) ||
(cipher_nid != NID_aes_128_ccm &&
!EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) {
@@ -457,24 +646,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
SSL *ssl = tls;
- ngtcp2_transport_params_type exttype =
- ngtcp2_conn_is_server(conn)
- ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
- : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS;
const uint8_t *tp;
size_t tplen;
- ngtcp2_transport_params params;
int rv;
SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
- rv = ngtcp2_decode_transport_params(&params, exttype, tp, tplen);
- if (rv != 0) {
- ngtcp2_conn_set_tls_error(conn, rv);
- return -1;
- }
-
- rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
if (rv != 0) {
ngtcp2_conn_set_tls_error(conn, rv);
return -1;
@@ -505,6 +683,7 @@ ngtcp2_crypto_level ngtcp2_crypto_openssl_from_ossl_encryption_level(
return NGTCP2_CRYPTO_LEVEL_APPLICATION;
default:
assert(0);
+ abort(); /* if NDEBUG is set */
}
}
@@ -522,5 +701,107 @@ ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
return ssl_encryption_early_data;
default:
assert(0);
+ abort(); /* if NDEBUG is set */
}
}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ if (RAND_bytes(data, (int)datalen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
+ const uint8_t *rx_secret,
+ const uint8_t *tx_secret, size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
+
+ if (rx_secret &&
+ ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ rx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ if (tx_secret &&
+ ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ tx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
+ int rv;
+
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int flush_flight(SSL *ssl) {
+ (void)ssl;
+ return 1;
+}
+
+static int send_alert(SSL *ssl, enum ssl_encryption_level_t level,
+ uint8_t alert) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)level;
+
+ ngtcp2_conn_set_tls_alert(conn, alert);
+
+ return 1;
+}
+
+static SSL_QUIC_METHOD quic_method = {
+ set_encryption_secrets,
+ add_handshake_data,
+ flush_flight,
+ send_alert,
+};
+
+static void crypto_openssl_configure_context(SSL_CTX *ssl_ctx) {
+ SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+}
+
+int ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx) {
+ crypto_openssl_configure_context(ssl_ctx);
+
+ return 0;
+}
+
+int ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx) {
+ crypto_openssl_configure_context(ssl_ctx);
+
+ return 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c
new file mode 100644
index 00000000000..32d17adc6c3
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c
@@ -0,0 +1,697 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+#include <string.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_picotls.h>
+
+#include <picotls.h>
+#include <picotls/openssl.h>
+
+#include "shared.h"
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm);
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)&ptls_openssl_sha256;
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)&ptls_openssl_aes128gcm);
+ ctx->md.native_handle = (void *)&ptls_openssl_sha256;
+ ctx->hp.native_handle = (void *)&ptls_openssl_aes128ctr;
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ ptls_aead_algorithm_t *alg = aead_native_handle;
+
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = alg->tag_size;
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm);
+}
+
+static const ptls_aead_algorithm_t *crypto_ptls_get_aead(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ return cs->aead;
+}
+
+static uint64_t crypto_ptls_get_aead_max_encryption(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ if (cs->aead == &ptls_openssl_aes128gcm ||
+ cs->aead == &ptls_openssl_aes256gcm) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ }
+
+ if (cs->aead == &ptls_openssl_chacha20poly1305) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ }
+
+ return 0;
+}
+
+static uint64_t crypto_ptls_get_aead_max_decryption_failure(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ if (cs->aead == &ptls_openssl_aes128gcm ||
+ cs->aead == &ptls_openssl_aes256gcm) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ }
+
+ if (cs->aead == &ptls_openssl_chacha20poly1305) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ }
+
+ return 0;
+}
+
+static const ptls_cipher_algorithm_t *crypto_ptls_get_hp(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ if (cs->aead == &ptls_openssl_aes128gcm) {
+ return &ptls_openssl_aes128ctr;
+ }
+
+ if (cs->aead == &ptls_openssl_aes256gcm) {
+ return &ptls_openssl_aes256ctr;
+ }
+
+ if (cs->aead == &ptls_openssl_chacha20poly1305) {
+ return &ptls_openssl_chacha20;
+ }
+
+ return NULL;
+}
+
+static const ptls_hash_algorithm_t *crypto_ptls_get_md(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ return cs->hash;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ ngtcp2_crypto_picotls_ctx *cptls = tls_native_handle;
+ ngtcp2_crypto_aead_init(&ctx->aead,
+ (void *)crypto_ptls_get_aead(cptls->ptls));
+ ctx->md.native_handle = (void *)crypto_ptls_get_md(cptls->ptls);
+ ctx->hp.native_handle = (void *)crypto_ptls_get_hp(cptls->ptls);
+ ctx->max_encryption = crypto_ptls_get_aead_max_encryption(cptls->ptls);
+ ctx->max_decryption_failure =
+ crypto_ptls_get_aead_max_decryption_failure(cptls->ptls);
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
+}
+
+static size_t crypto_md_hashlen(const ptls_hash_algorithm_t *md) {
+ return md->digest_size;
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return crypto_md_hashlen(md->native_handle);
+}
+
+static size_t crypto_aead_keylen(const ptls_aead_algorithm_t *aead) {
+ return aead->key_size;
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_keylen(aead->native_handle);
+}
+
+static size_t crypto_aead_noncelen(const ptls_aead_algorithm_t *aead) {
+ return aead->iv_size;
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_noncelen(aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const ptls_aead_algorithm_t *cipher = aead->native_handle;
+ size_t keylen = crypto_aead_keylen(cipher);
+ ptls_aead_context_t *actx;
+ static const uint8_t iv[PTLS_MAX_IV_SIZE] = {0};
+
+ (void)noncelen;
+ (void)keylen;
+
+ actx = ptls_aead_new_direct(cipher, /* is_enc = */ 1, key, iv);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const ptls_aead_algorithm_t *cipher = aead->native_handle;
+ size_t keylen = crypto_aead_keylen(cipher);
+ ptls_aead_context_t *actx;
+ const uint8_t iv[PTLS_MAX_IV_SIZE] = {0};
+
+ (void)noncelen;
+ (void)keylen;
+
+ actx = ptls_aead_new_direct(cipher, /* is_enc = */ 0, key, iv);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+
+ return 0;
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ ptls_aead_free(aead_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ ptls_cipher_context_t *actx;
+
+ actx = ptls_cipher_new(cipher->native_handle, /* is_enc = */ 1, key);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ cipher_ctx->native_handle = actx;
+
+ return 0;
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (cipher_ctx->native_handle) {
+ ptls_cipher_free(cipher_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+ ptls_iovec_t saltv, ikm;
+
+ saltv = ptls_iovec_init(salt, saltlen);
+ ikm = ptls_iovec_init(secret, secretlen);
+
+ if (ptls_hkdf_extract(md->native_handle, dest, saltv, ikm) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+ ptls_iovec_t prk, infov;
+
+ prk = ptls_iovec_init(secret, secretlen);
+ infov = ptls_iovec_init(info, infolen);
+
+ if (ptls_hkdf_expand(md->native_handle, dest, destlen, prk, infov) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ ptls_iovec_t saltv, ikm, prk, infov;
+ uint8_t prkbuf[PTLS_MAX_DIGEST_SIZE];
+ ptls_hash_algorithm_t *algo = md->native_handle;
+
+ saltv = ptls_iovec_init(salt, saltlen);
+ ikm = ptls_iovec_init(secret, secretlen);
+
+ if (ptls_hkdf_extract(algo, prkbuf, saltv, ikm) != 0) {
+ return -1;
+ }
+
+ prk = ptls_iovec_init(prkbuf, algo->digest_size);
+ infov = ptls_iovec_init(info, infolen);
+
+ if (ptls_hkdf_expand(algo, dest, destlen, prk, infov) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ ptls_aead_context_t *actx = aead_ctx->native_handle;
+
+ (void)aead;
+
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ ptls_aead_encrypt(actx, dest, plaintext, plaintextlen, 0, aad, aadlen);
+
+ /* zero-out static iv once again */
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ ptls_aead_context_t *actx = aead_ctx->native_handle;
+
+ (void)aead;
+
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ if (ptls_aead_decrypt(actx, dest, ciphertext, ciphertextlen, 0, aad,
+ aadlen) == SIZE_MAX) {
+ return -1;
+ }
+
+ /* zero-out static iv once again */
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ ptls_cipher_context_t *actx = hp_ctx->native_handle;
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+
+ (void)hp;
+
+ ptls_cipher_init(actx, sample);
+ ptls_cipher_encrypt(actx, dest, PLAINTEXT, sizeof(PLAINTEXT) - 1);
+
+ return 0;
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_picotls_ctx *cptls = ngtcp2_conn_get_tls_native_handle(conn);
+ ptls_buffer_t sendbuf;
+ size_t epoch_offsets[5] = {0};
+ size_t epoch = ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(crypto_level);
+ size_t epoch_datalen;
+ size_t i;
+ int rv;
+
+ ptls_buffer_init(&sendbuf, (void *)"", 0);
+
+ assert(epoch == ptls_get_read_epoch(cptls->ptls));
+
+ rv = ptls_handle_message(cptls->ptls, &sendbuf, epoch_offsets, epoch, data,
+ datalen, &cptls->handshake_properties);
+ if (rv != 0 && rv != PTLS_ERROR_IN_PROGRESS) {
+ if (PTLS_ERROR_GET_CLASS(rv) == PTLS_ERROR_CLASS_SELF_ALERT) {
+ ngtcp2_conn_set_tls_alert(conn, (uint8_t)PTLS_ERROR_TO_ALERT(rv));
+ }
+
+ rv = -1;
+ goto fin;
+ }
+
+ if (!ngtcp2_conn_is_server(conn) &&
+ cptls->handshake_properties.client.early_data_acceptance ==
+ PTLS_EARLY_DATA_REJECTED) {
+ ngtcp2_conn_early_data_rejected(conn);
+ }
+
+ for (i = 0; i < 4; ++i) {
+ epoch_datalen = epoch_offsets[i + 1] - epoch_offsets[i];
+ if (epoch_datalen == 0) {
+ continue;
+ }
+
+ assert(i != 1);
+
+ if (ngtcp2_conn_submit_crypto_data(
+ conn, ngtcp2_crypto_picotls_from_epoch(i),
+ sendbuf.base + epoch_offsets[i], epoch_datalen) != 0) {
+ rv = -1;
+ goto fin;
+ }
+ }
+
+ if (rv == 0) {
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = 0;
+
+fin:
+ ptls_buffer_dispose(&sendbuf);
+
+ return rv;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ (void)conn;
+ (void)tls;
+
+ /* The remote transport parameters will be set via picotls
+ collected_extensions callback */
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ (void)tls;
+ (void)buf;
+ (void)len;
+
+ /* The local transport parameters will be set in an external
+ call. */
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_picotls_from_epoch(size_t epoch) {
+ switch (epoch) {
+ case 0:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case 1:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case 2:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case 3:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return 0;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return 1;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return 2;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return 3;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ptls_openssl_random_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN);
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ ptls_openssl_random_bytes(data, datalen);
+
+ return 0;
+}
+
+void ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls) {
+ cptls->ptls = NULL;
+ memset(&cptls->handshake_properties, 0, sizeof(cptls->handshake_properties));
+}
+
+static int set_additional_extensions(ptls_handshake_properties_t *hsprops,
+ ngtcp2_conn *conn) {
+ const size_t buflen = 256;
+ uint8_t *buf;
+ ngtcp2_ssize nwrite;
+ ptls_raw_extension_t *exts = hsprops->additional_extensions;
+
+ assert(exts);
+
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ return -1;
+ }
+
+ nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, buflen);
+ if (nwrite < 0) {
+ goto fail;
+ }
+
+ exts[0].type = NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1;
+ exts[0].data.base = buf;
+ exts[0].data.len = (size_t)nwrite;
+
+ return 0;
+
+fail:
+ free(buf);
+
+ return -1;
+}
+
+int ngtcp2_crypto_picotls_collect_extension(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ uint16_t type) {
+ (void)ptls;
+ (void)properties;
+
+ return type == NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1;
+}
+
+int ngtcp2_crypto_picotls_collected_extensions(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ ptls_raw_extension_t *extensions) {
+ ngtcp2_crypto_conn_ref *conn_ref;
+ ngtcp2_conn *conn;
+ int rv;
+
+ (void)properties;
+
+ for (; extensions->type != UINT16_MAX; ++extensions) {
+ if (extensions->type != NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1) {
+ continue;
+ }
+
+ conn_ref = *ptls_get_data_ptr(ptls);
+ conn = conn_ref->get_conn(conn_ref);
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, extensions->data.base,
+ extensions->data.len);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static int update_traffic_key_server_cb(ptls_update_traffic_key_t *self,
+ ptls_t *ptls, int is_enc, size_t epoch,
+ const void *secret) {
+ ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch);
+ ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls);
+ size_t secretlen = cipher->hash->digest_size;
+ ngtcp2_crypto_picotls_ctx *cptls;
+
+ (void)self;
+
+ if (is_enc) {
+ if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (level == NGTCP2_CRYPTO_LEVEL_HANDSHAKE) {
+ /* libngtcp2 allows an application to change QUIC transport
+ * parameters before installing Handshake tx key. We need to
+ * wait for the key to get the correct local transport
+ * parameters from ngtcp2_conn.
+ */
+ cptls = ngtcp2_conn_get_tls_native_handle(conn);
+
+ if (set_additional_extensions(&cptls->handshake_properties, conn) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static ptls_update_traffic_key_t update_traffic_key_server = {
+ update_traffic_key_server_cb,
+};
+
+static int update_traffic_key_cb(ptls_update_traffic_key_t *self, ptls_t *ptls,
+ int is_enc, size_t epoch, const void *secret) {
+ ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch);
+ ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls);
+ size_t secretlen = cipher->hash->digest_size;
+
+ (void)self;
+
+ if (is_enc) {
+ if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static ptls_update_traffic_key_t update_traffic_key = {update_traffic_key_cb};
+
+int ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx) {
+ ctx->max_early_data_size = UINT32_MAX;
+ ctx->omit_end_of_early_data = 1;
+ ctx->update_traffic_key = &update_traffic_key_server;
+
+ return 0;
+}
+
+int ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx) {
+ ctx->omit_end_of_early_data = 1;
+ ctx->update_traffic_key = &update_traffic_key;
+
+ return 0;
+}
+
+int ngtcp2_crypto_picotls_configure_server_session(
+ ngtcp2_crypto_picotls_ctx *cptls) {
+ ptls_handshake_properties_t *hsprops = &cptls->handshake_properties;
+
+ hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension;
+ hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions;
+
+ return 0;
+}
+
+int ngtcp2_crypto_picotls_configure_client_session(
+ ngtcp2_crypto_picotls_ctx *cptls, ngtcp2_conn *conn) {
+ ptls_handshake_properties_t *hsprops = &cptls->handshake_properties;
+
+ hsprops->client.max_early_data_size = calloc(1, sizeof(uint32_t));
+ if (hsprops->client.max_early_data_size == NULL) {
+ return -1;
+ }
+
+ if (set_additional_extensions(hsprops, conn) != 0) {
+ free(hsprops->client.max_early_data_size);
+ hsprops->client.max_early_data_size = NULL;
+ return -1;
+ }
+
+ hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension;
+ hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions;
+
+ return 0;
+}
+
+void ngtcp2_crypto_picotls_deconfigure_session(
+ ngtcp2_crypto_picotls_ctx *cptls) {
+ ptls_handshake_properties_t *hsprops;
+ ptls_raw_extension_t *exts;
+
+ if (cptls == NULL) {
+ return;
+ }
+
+ hsprops = &cptls->handshake_properties;
+
+ free(hsprops->client.max_early_data_size);
+
+ exts = hsprops->additional_extensions;
+ if (exts) {
+ free(hsprops->additional_extensions[0].data.base);
+ }
+}
diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.c b/deps/ngtcp2/ngtcp2/crypto/shared.c
index 5d040f2ce75..78252b852b4 100644
--- a/deps/ngtcp2/ngtcp2/crypto/shared.c
+++ b/deps/ngtcp2/ngtcp2/crypto/shared.c
@@ -24,10 +24,18 @@
*/
#include "shared.h"
+#ifdef WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <netinet/in.h>
+#endif
+
#include <string.h>
#include <assert.h>
#include "ngtcp2_macro.h"
+#include "ngtcp2_net.h"
ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
void *md_native_handle) {
@@ -78,10 +86,16 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
ngtcp2_crypto_ctx_initial(&ctx);
- if (version == NGTCP2_PROTO_VER_V1) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1;
saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1;
- } else {
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2_DRAFT;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V2_DRAFT) - 1;
+ break;
+ default:
salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT;
saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1;
}
@@ -119,27 +133,55 @@ size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) {
}
int ngtcp2_crypto_derive_packet_protection_key(
- uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead,
- const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) {
- static const uint8_t KEY_LABEL[] = "quic key";
- static const uint8_t IV_LABEL[] = "quic iv";
- static const uint8_t HP_KEY_LABEL[] = "quic hp";
+ uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version,
+ const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen) {
+ static const uint8_t KEY_LABEL_V1[] = "quic key";
+ static const uint8_t IV_LABEL_V1[] = "quic iv";
+ static const uint8_t HP_KEY_LABEL_V1[] = "quic hp";
+ static const uint8_t KEY_LABEL_V2_DRAFT[] = "quicv2 key";
+ static const uint8_t IV_LABEL_V2_DRAFT[] = "quicv2 iv";
+ static const uint8_t HP_KEY_LABEL_V2_DRAFT[] = "quicv2 hp";
size_t keylen = ngtcp2_crypto_aead_keylen(aead);
size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+ const uint8_t *key_label;
+ size_t key_labellen;
+ const uint8_t *iv_label;
+ size_t iv_labellen;
+ const uint8_t *hp_key_label;
+ size_t hp_key_labellen;
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ key_label = KEY_LABEL_V2_DRAFT;
+ key_labellen = sizeof(KEY_LABEL_V2_DRAFT) - 1;
+ iv_label = IV_LABEL_V2_DRAFT;
+ iv_labellen = sizeof(IV_LABEL_V2_DRAFT) - 1;
+ hp_key_label = HP_KEY_LABEL_V2_DRAFT;
+ hp_key_labellen = sizeof(HP_KEY_LABEL_V2_DRAFT) - 1;
+ break;
+ default:
+ key_label = KEY_LABEL_V1;
+ key_labellen = sizeof(KEY_LABEL_V1) - 1;
+ iv_label = IV_LABEL_V1;
+ iv_labellen = sizeof(IV_LABEL_V1) - 1;
+ hp_key_label = HP_KEY_LABEL_V1;
+ hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1;
+ }
if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen,
- KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) {
+ key_label, key_labellen) != 0) {
return -1;
}
if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen,
- IV_LABEL, sizeof(IV_LABEL) - 1) != 0) {
+ iv_label, iv_labellen) != 0) {
return -1;
}
- if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label(
- hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL,
- sizeof(HP_KEY_LABEL) - 1) != 0) {
+ if (hp_key != NULL &&
+ ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen,
+ hp_key_label, hp_key_labellen) != 0) {
return -1;
}
@@ -176,6 +218,7 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
size_t ivlen;
int rv;
ngtcp2_crypto_ctx cctx;
+ uint32_t version;
if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) {
return 0;
@@ -191,12 +234,25 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
hp_key = hp_keybuf;
}
- if (level == NGTCP2_CRYPTO_LEVEL_EARLY) {
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
ngtcp2_crypto_ctx_tls_early(&cctx, tls);
ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
- } else {
+ version = ngtcp2_conn_get_client_chosen_version(conn);
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ if (ngtcp2_conn_is_server(conn) &&
+ !ngtcp2_conn_get_negotiated_version(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ return -1;
+ }
+ }
+ /* fall through */
+ default:
ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ version = ngtcp2_conn_get_negotiated_version(conn);
if (!ctx->aead.native_handle) {
ngtcp2_crypto_ctx_tls(&cctx, tls);
@@ -210,8 +266,8 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
hp = &ctx->hp;
ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
- if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md,
- secret, secretlen) != 0) {
+ if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
+ md, secret, secretlen) != 0) {
return -1;
}
@@ -272,17 +328,10 @@ fail:
* This function returns 0 if it succeeds, or -1.
*/
static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) {
- ngtcp2_transport_params_type exttype =
- ngtcp2_conn_is_server(conn)
- ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
- : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO;
- ngtcp2_transport_params params;
ngtcp2_ssize nwrite;
uint8_t buf[256];
- ngtcp2_conn_get_local_transport_params(conn, &params);
-
- nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, &params);
+ nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf));
if (nwrite < 0) {
return -1;
}
@@ -310,6 +359,7 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
size_t ivlen;
int rv;
ngtcp2_crypto_ctx cctx;
+ uint32_t version;
if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) {
return 0;
@@ -325,12 +375,25 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
hp_key = hp_keybuf;
}
- if (level == NGTCP2_CRYPTO_LEVEL_EARLY) {
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
ngtcp2_crypto_ctx_tls_early(&cctx, tls);
ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
- } else {
+ version = ngtcp2_conn_get_client_chosen_version(conn);
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ if (ngtcp2_conn_is_server(conn) &&
+ !ngtcp2_conn_get_negotiated_version(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ return -1;
+ }
+ }
+ /* fall through */
+ default:
ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ version = ngtcp2_conn_get_negotiated_version(conn);
if (!ctx->aead.native_handle) {
ngtcp2_crypto_ctx_tls(&cctx, tls);
@@ -344,8 +407,8 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
hp = &ctx->hp;
ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
- if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md,
- secret, secretlen) != 0) {
+ if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
+ md, secret, secretlen) != 0) {
return -1;
}
@@ -371,15 +434,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
goto fail;
}
- if (ngtcp2_conn_is_server(conn)) {
- rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
- if (rv != 0) {
- return rv;
- }
-
- if (crypto_set_local_transport_params(conn, tls) != 0) {
- return rv;
- }
+ if (ngtcp2_conn_is_server(conn) &&
+ crypto_set_local_transport_params(conn, tls) != 0) {
+ goto fail;
}
break;
@@ -408,7 +465,7 @@ int ngtcp2_crypto_derive_and_install_initial_key(
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
- const ngtcp2_cid *client_dcid) {
+ uint32_t version, const ngtcp2_cid *client_dcid) {
uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
@@ -427,7 +484,6 @@ int ngtcp2_crypto_derive_and_install_initial_key(
ngtcp2_crypto_aead_ctx retry_aead_ctx = {0};
int rv;
int server = ngtcp2_conn_is_server(conn);
- uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
const uint8_t *retry_key;
size_t retry_noncelen;
@@ -472,13 +528,13 @@ int ngtcp2_crypto_derive_and_install_initial_key(
}
if (ngtcp2_crypto_derive_packet_protection_key(
- rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret,
+ rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret,
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
return -1;
}
if (ngtcp2_crypto_derive_packet_protection_key(
- tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret,
+ tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
return -1;
}
@@ -506,10 +562,16 @@ int ngtcp2_crypto_derive_and_install_initial_key(
if (!server && !ngtcp2_conn_after_retry(conn)) {
ngtcp2_crypto_aead_retry(&retry_aead);
- if (ngtcp2_conn_get_negotiated_version(conn) == NGTCP2_PROTO_VER_V1) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
- } else {
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+ break;
+ default:
retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
}
@@ -543,6 +605,114 @@ fail:
return -1;
}
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
+ uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
+ uint32_t version, const ngtcp2_cid *client_dcid) {
+ uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn);
+ ngtcp2_crypto_aead_ctx rx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0};
+ ngtcp2_crypto_aead_ctx tx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0};
+ int rv;
+ int server = ngtcp2_conn_is_server(conn);
+
+ if (!rx_secret) {
+ rx_secret = rx_secretbuf;
+ }
+ if (!tx_secret) {
+ tx_secret = tx_secretbuf;
+ }
+ if (!initial_secret) {
+ initial_secret = initial_secretbuf;
+ }
+
+ if (!rx_key) {
+ rx_key = rx_keybuf;
+ }
+ if (!rx_iv) {
+ rx_iv = rx_ivbuf;
+ }
+ if (!rx_hp_key) {
+ rx_hp_key = rx_hp_keybuf;
+ }
+ if (!tx_key) {
+ tx_key = tx_keybuf;
+ }
+ if (!tx_iv) {
+ tx_iv = tx_ivbuf;
+ }
+ if (!tx_hp_key) {
+ tx_hp_key = tx_hp_keybuf;
+ }
+
+ if (ngtcp2_crypto_derive_initial_secrets(
+ version, rx_secret, tx_secret, initial_secret, client_dcid,
+ server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ rv = ngtcp2_conn_install_vneg_initial_key(
+ conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv,
+ &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx);
+ ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx);
+
+ return -1;
+}
+
int ngtcp2_crypto_update_key(
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv,
@@ -553,14 +723,15 @@ int ngtcp2_crypto_update_key(
const ngtcp2_crypto_aead *aead = &ctx->aead;
const ngtcp2_crypto_md *md = &ctx->md;
size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+ uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret,
secretlen) != 0) {
return -1;
}
- if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md,
- rx_secret, secretlen) != 0) {
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) {
return -1;
}
@@ -569,8 +740,8 @@ int ngtcp2_crypto_update_key(
return -1;
}
- if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md,
- tx_secret, secretlen) != 0) {
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) {
return -1;
}
@@ -593,9 +764,9 @@ int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *plaintext, size_t plaintextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen) {
+ const uint8_t *aad, size_t aadlen) {
if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen,
- nonce, noncelen, ad, adlen) != 0) {
+ nonce, noncelen, aad, aadlen) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
@@ -605,10 +776,10 @@ int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *ciphertext, size_t ciphertextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen) {
+ const uint8_t *aad, size_t aadlen) {
if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen,
- nonce, noncelen, ad, adlen) != 0) {
- return NGTCP2_ERR_TLS_DECRYPT;
+ nonce, noncelen, aad, aadlen) != 0) {
+ return NGTCP2_ERR_DECRYPT;
}
return 0;
}
@@ -643,32 +814,413 @@ int ngtcp2_crypto_update_key_cb(
}
int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token,
- const ngtcp2_crypto_md *md,
const uint8_t *secret,
size_t secretlen,
const ngtcp2_cid *cid) {
- uint8_t buf[64];
+ static const uint8_t info[] = "stateless_reset";
+ ngtcp2_crypto_md md;
+
+ if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN,
+ ngtcp2_crypto_md_sha256(&md), secret, secretlen,
+ cid->data, cid->datalen, info,
+ sizeof(info) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
+ size_t ivlen, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen,
+ const uint8_t *info_prefix,
+ size_t info_prefixlen) {
+ static const uint8_t key_info_suffix[] = " key";
+ static const uint8_t iv_info_suffix[] = " iv";
+ uint8_t intsecret[32];
+ uint8_t info[32];
+ uint8_t *p;
+
+ assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret));
+ assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info));
+ assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info));
+
+ if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt,
+ saltlen) != 0) {
+ return -1;
+ }
+
+ memcpy(info, info_prefix, info_prefixlen);
+ p = info + info_prefixlen;
+
+ memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1);
+ p += sizeof(key_info_suffix) - 1;
+
+ if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret),
+ info, (size_t)(p - info)) != 0) {
+ return -1;
+ }
+
+ p = info + info_prefixlen;
+
+ memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1);
+ p += sizeof(iv_info_suffix) - 1;
+
+ if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret),
+ info, (size_t)(p - info)) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version,
+ const ngtcp2_sockaddr *sa,
+ ngtcp2_socklen salen,
+ const ngtcp2_cid *retry_scid) {
+ uint8_t *p = dest;
+
+ version = ngtcp2_htonl(version);
+ memcpy(p, &version, sizeof(version));
+ memcpy(p, sa, (size_t)salen);
+ p += salen;
+ memcpy(p, retry_scid->data, retry_scid->datalen);
+ p += retry_scid->datalen;
+
+ return (size_t)(p - dest);
+}
+
+static const uint8_t retry_token_info_prefix[] = "retry_token";
+
+ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) {
+ uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
+ uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ size_t plaintextlen;
+ uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+ NGTCP2_MAX_CIDLEN];
+ size_t aadlen;
+ uint8_t *p = plaintext;
+ ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
int rv;
- assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf));
- assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf));
+ memset(plaintext, 0, sizeof(plaintext));
+
+ *p++ = (uint8_t)odcid->datalen;
+ memcpy(p, odcid->data, odcid->datalen);
+ p += NGTCP2_MAX_CIDLEN;
+ memcpy(p, &ts_be, sizeof(ts_be));
+ p += sizeof(ts_be);
+
+ plaintextlen = (size_t)(p - plaintext);
+
+ if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
+ return -1;
+ }
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ assert(sizeof(key) >= keylen);
+ assert(sizeof(iv) >= ivlen);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, sizeof(rand_data),
+ retry_token_info_prefix,
+ sizeof(retry_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+ remote_addrlen, retry_scid);
+
+ p = token;
+ *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY;
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
+ ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
- rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data,
- cid->datalen);
if (rv != 0) {
return -1;
}
- memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN);
+ p += plaintextlen + aead.max_overhead;
+ memcpy(p, rand_data, sizeof(rand_data));
+ p += sizeof(rand_data);
+
+ return p - token;
+}
+
+int ngtcp2_crypto_verify_retry_token(
+ ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
+ uint8_t
+ plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+ NGTCP2_MAX_CIDLEN];
+ size_t aadlen;
+ const uint8_t *rand_data;
+ const uint8_t *ciphertext;
+ size_t ciphertextlen;
+ size_t cil;
+ int rv;
+ ngtcp2_tstamp gen_ts;
+
+ if (tokenlen != NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN ||
+ token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) {
+ return -1;
+ }
+
+ rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+ ciphertext = token + 1;
+ ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
+ retry_token_info_prefix,
+ sizeof(retry_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+ remote_addrlen, dcid);
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
+ ciphertextlen, iv, ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ cil = plaintext[0];
+
+ assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN));
+
+ memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN,
+ sizeof(gen_ts));
+
+ gen_ts = ngtcp2_ntohl64(gen_ts);
+ if (gen_ts + timeout <= ts) {
+ return -1;
+ }
+
+ ngtcp2_cid_init(odcid, plaintext + /* cid len = */ 1, cil);
return 0;
}
-ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen,
- uint32_t version,
- const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid,
- uint64_t error_code) {
+static size_t crypto_generate_regular_token_aad(uint8_t *dest,
+ const ngtcp2_sockaddr *sa) {
+ const uint8_t *addr;
+ size_t addrlen;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr;
+ addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr);
+ break;
+ case AF_INET6:
+ addr =
+ (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr;
+ addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr);
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ memcpy(dest, addr, addrlen);
+
+ return addrlen;
+}
+
+static const uint8_t regular_token_info_prefix[] = "regular_token";
+
+ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ ngtcp2_tstamp ts) {
+ uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+ uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ size_t plaintextlen;
+ uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
+ size_t aadlen;
+ uint8_t *p = plaintext;
+ ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
+ int rv;
+ (void)remote_addrlen;
+
+ memcpy(p, &ts_be, sizeof(ts_be));
+ p += sizeof(ts_be);
+
+ plaintextlen = (size_t)(p - plaintext);
+
+ if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
+ return -1;
+ }
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ assert(sizeof(key) >= keylen);
+ assert(sizeof(iv) >= ivlen);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, sizeof(rand_data),
+ regular_token_info_prefix,
+ sizeof(regular_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
+
+ p = token;
+ *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR;
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
+ ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ p += plaintextlen + aead.max_overhead;
+ memcpy(p, rand_data, sizeof(rand_data));
+ p += sizeof(rand_data);
+
+ return p - token;
+}
+
+int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen,
+ ngtcp2_duration timeout,
+ ngtcp2_tstamp ts) {
+ uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
+ size_t aadlen;
+ const uint8_t *rand_data;
+ const uint8_t *ciphertext;
+ size_t ciphertextlen;
+ int rv;
+ ngtcp2_tstamp gen_ts;
+ (void)remote_addrlen;
+
+ if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
+ token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) {
+ return -1;
+ }
+
+ rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+ ciphertext = token + 1;
+ ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
+ regular_token_info_prefix,
+ sizeof(regular_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
+ ciphertextlen, iv, ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ memcpy(&gen_ts, plaintext, sizeof(gen_ts));
+
+ gen_ts = ngtcp2_ntohl64(gen_ts);
+ if (gen_ts + timeout <= ts) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_ssize ngtcp2_crypto_write_connection_close(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen) {
uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
@@ -689,7 +1241,7 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen,
}
if (ngtcp2_crypto_derive_packet_protection_key(
- tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret,
+ tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
return -1;
}
@@ -706,8 +1258,9 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen,
}
spktlen = ngtcp2_pkt_write_connection_close(
- dest, destlen, version, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb,
- &ctx.aead, &aead_ctx, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx);
+ dest, destlen, version, dcid, scid, error_code, reason, reasonlen,
+ ngtcp2_crypto_encrypt_cb, &ctx.aead, &aead_ctx, tx_iv,
+ ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx);
if (spktlen < 0) {
spktlen = -1;
}
@@ -732,10 +1285,16 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
ngtcp2_crypto_aead_retry(&aead);
- if (version == NGTCP2_PROTO_VER_V1) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
- } else {
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+ break;
+ default:
key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
}
@@ -757,22 +1316,14 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
return spktlen;
}
-/*
- * crypto_setup_initial_crypto establishes the initial secrets and
- * encryption keys, and prepares local QUIC transport parameters.
- */
-static int crypto_setup_initial_crypto(ngtcp2_conn *conn,
- const ngtcp2_cid *dcid) {
- return ngtcp2_crypto_derive_and_install_initial_key(
- conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid);
-}
-
int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) {
const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn);
void *tls = ngtcp2_conn_get_tls_native_handle(conn);
(void)user_data;
- if (crypto_setup_initial_crypto(conn, dcid) != 0) {
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -792,9 +1343,9 @@ int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
void *user_data) {
(void)user_data;
- if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- &hd->scid) != 0) {
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -806,7 +1357,23 @@ int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
void *user_data) {
(void)user_data;
- if (crypto_setup_initial_crypto(conn, dcid) != 0) {
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data) {
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version,
+ client_dcid) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -829,3 +1396,23 @@ void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
ngtcp2_crypto_cipher_ctx_free(cipher_ctx);
}
+
+int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data) {
+ int rv;
+ (void)offset;
+ (void)user_data;
+
+ if (ngtcp2_crypto_read_write_crypto_data(conn, crypto_level, data, datalen) !=
+ 0) {
+ rv = ngtcp2_conn_get_tls_error(conn);
+ if (rv) {
+ return rv;
+ }
+ return NGTCP2_ERR_CRYPTO;
+ }
+
+ return 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.h b/deps/ngtcp2/ngtcp2/crypto/shared.h
index b7fe2f15da9..02b948901ae 100644
--- a/deps/ngtcp2/ngtcp2/crypto/shared.h
+++ b/deps/ngtcp2/ngtcp2/crypto/shared.h
@@ -51,6 +51,16 @@
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \
"\x7f\x0a"
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_V2_DRAFT` is a salt value which is used to
+ * derive initial secret. It is used for QUIC v2 draft.
+ */
+#define NGTCP2_INITIAL_SALT_V2_DRAFT \
+ "\xa7\x07\xc2\x03\xa5\x9b\x47\x18\x4a\x1d\x62\xca\x57\x04\x06\xea\x7a\xe3" \
+ "\xe5\xd3"
+
/* Maximum key usage (encryption) limits */
#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
@@ -65,6 +75,40 @@
/**
* @function
*
+ * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
+ * encryption and decryption.
+ */
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_init` initializes |aead| with the provided
+ * |aead_native_handle| which is an underlying AEAD object.
+ *
+ * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
+ * a pointer to EVP_CIPHER.
+ *
+ * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
+ * gnutls_cipher_algorithm_t casted to ``void *``.
+ *
+ * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
+ * be a pointer to EVP_AEAD.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
+ * AEAD_AES_128_GCM for Retry packet integrity protection.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
* `ngtcp2_crypto_derive_initial_secrets` derives initial secrets.
* |rx_secret| and |tx_secret| must point to the buffer of at least 32
* bytes capacity. rx for read and tx for write. This function
@@ -87,6 +131,34 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
/**
* @function
*
+ * `ngtcp2_crypto_derive_packet_protection_key` derives packet
+ * protection key. This function writes packet protection key into
+ * the buffer pointed by |key|. The length of derived key is
+ * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>`
+ * bytes. |key| must have enough capacity to store the key. This
+ * function writes packet protection IV into |iv|. The length of
+ * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` bytes. |iv| must have
+ * enough capacity to store the IV.
+ *
+ * If |hp| is not NULL, this function also derives packet header
+ * protection key and writes the key into the buffer pointed by |hp|.
+ * The length of derived key is `ngtcp2_crypto_aead_keylen(aead)
+ * <ngtcp2_crypto_aead_keylen>` bytes. |hp|, if not NULL, must have
+ * enough capacity to store the key.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv,
+ uint8_t *hp, uint32_t version,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
* `ngtcp2_crypto_update_traffic_secret` derives the next generation
* of the traffic secret. |secret| specifies the current secret and
* its length is given in |secretlen|. The length of new key is the
@@ -183,7 +255,58 @@ int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls);
int ngtcp2_crypto_derive_and_install_initial_key(
ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
- uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp,
+ uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
+ const ngtcp2_cid *client_dcid);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial
+ * keying materials and installs keys to |conn|. This function is
+ * dedicated to install keys for |version| which is negotiated, or
+ * being negotiated.
+ *
+ * If |rx_secret| is not NULL, the secret for decryption is written to
+ * the buffer pointed by |rx_secret|. The length of secret is 32
+ * bytes, and |rx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |tx_secret| is not NULL, the secret for encryption is written to
+ * the buffer pointed by |tx_secret|. The length of secret is 32
+ * bytes, and |tx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |initial_secret| is not NULL, the initial secret is written to
+ * the buffer pointed by |initial_secret|. The length of secret is 32
+ * bytes, and |initial_secret| must point to the buffer which has
+ * enough capacity.
+ *
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client.
+ *
+ * If |rx_key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |rx_key|. If
+ * |rx_iv| is not NULL, the derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp|
+ * is not NULL, the derived header protection key for decryption is
+ * written to the buffer pointed by |rx_hp|.
+ *
+ * If |tx_key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |tx_key|. If
+ * |tx_iv| is not NULL, the derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp|
+ * is not NULL, the derived header protection key for encryption is
+ * written to the buffer pointed by |tx_hp|.
+ *
+ * The length of packet protection key and header protection key is 16
+ * bytes long. The length of packet protection IV is 12 bytes long.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
+ uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
const ngtcp2_cid *client_dcid);
/**
@@ -208,4 +331,20 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
*/
void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx);
+/*
+ * `ngtcp2_crypto_md_sha256` initializes |md| with SHA256 message
+ * digest algorithm and returns |md|.
+ */
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md);
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead);
+
+/*
+ * `ngtcp2_crypto_random` writes cryptographically-secure random
+ * |datalen| bytes into the buffer pointed by |data|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen);
+
#endif /* NGTCP2_SHARED_H */
diff --git a/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c b/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c
new file mode 100644
index 00000000000..9a58b9be2b7
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c
@@ -0,0 +1,524 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
+
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+
+#include "shared.h"
+
+#define PRINTF_DEBUG 0
+#if PRINTF_DEBUG
+# define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DEBUG_MSG(...) (void)0
+#endif
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm());
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)wolfSSL_EVP_sha256();
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_EVP_aes_128_gcm());
+ ctx->md.native_handle = (void *)wolfSSL_EVP_sha256();
+ ctx->hp.native_handle = (void *)wolfSSL_EVP_aes_128_ctr();
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = wolfSSL_quic_get_aead_tag_len(
+ (const WOLFSSL_EVP_CIPHER *)(aead_native_handle));
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm());
+}
+
+static uint64_t crypto_wolfssl_get_aead_max_encryption(WOLFSSL *ssl) {
+ const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl);
+
+ if (wolfSSL_quic_aead_is_gcm(aead)) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ }
+ if (wolfSSL_quic_aead_is_chacha20(aead)) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ }
+ if (wolfSSL_quic_aead_is_ccm(aead)) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
+ }
+ return 0;
+}
+
+static uint64_t crypto_wolfssl_get_aead_max_decryption_failure(WOLFSSL *ssl) {
+ const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl);
+
+ if (wolfSSL_quic_aead_is_gcm(aead)) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ }
+ if (wolfSSL_quic_aead_is_chacha20(aead)) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ }
+ if (wolfSSL_quic_aead_is_ccm(aead)) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
+ }
+ return 0;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ WOLFSSL *ssl = tls_native_handle;
+
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_quic_get_aead(ssl));
+ ctx->md.native_handle = (void *)wolfSSL_quic_get_md(ssl);
+ ctx->hp.native_handle = (void *)wolfSSL_quic_get_hp(ssl);
+ ctx->max_encryption = crypto_wolfssl_get_aead_max_encryption(ssl);
+ ctx->max_decryption_failure =
+ crypto_wolfssl_get_aead_max_decryption_failure(ssl);
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
+}
+
+static size_t crypto_md_hashlen(const WOLFSSL_EVP_MD *md) {
+ return (size_t)wolfSSL_EVP_MD_size(md);
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return crypto_md_hashlen(md->native_handle);
+}
+
+static size_t crypto_aead_keylen(const WOLFSSL_EVP_CIPHER *aead) {
+ return (size_t)wolfSSL_EVP_Cipher_key_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_keylen(aead->native_handle);
+}
+
+static size_t crypto_aead_noncelen(const WOLFSSL_EVP_CIPHER *aead) {
+ return (size_t)wolfSSL_EVP_CIPHER_iv_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_noncelen(aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle;
+ WOLFSSL_EVP_CIPHER_CTX *actx;
+ static const uint8_t iv[AES_BLOCK_SIZE] = {0};
+
+ (void)noncelen;
+ actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 1);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle;
+ WOLFSSL_EVP_CIPHER_CTX *actx;
+ static const uint8_t iv[AES_BLOCK_SIZE] = {0};
+
+ (void)noncelen;
+ actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 0);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+ return 0;
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ wolfSSL_EVP_CIPHER_CTX_free(aead_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ WOLFSSL_EVP_CIPHER_CTX *actx;
+
+ actx =
+ wolfSSL_quic_crypt_new(cipher->native_handle, key, NULL, /* encrypt */ 1);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ cipher_ctx->native_handle = actx;
+ return 0;
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (cipher_ctx->native_handle) {
+ wolfSSL_EVP_CIPHER_CTX_free(cipher_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+ if (wolfSSL_quic_hkdf_extract(dest, md->native_handle, secret, secretlen,
+ salt, saltlen) != WOLFSSL_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+ if (wolfSSL_quic_hkdf_expand(dest, destlen, md->native_handle, secret,
+ secretlen, info, infolen) != WOLFSSL_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ if (wolfSSL_quic_hkdf(dest, destlen, md->native_handle, secret, secretlen,
+ salt, saltlen, info, infolen) != WOLFSSL_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)aead;
+ (void)noncelen;
+ if (wolfSSL_quic_aead_encrypt(dest, aead_ctx->native_handle, plaintext,
+ plaintextlen, nonce, aad,
+ aadlen) != WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: encrypt FAILED\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)aead;
+ (void)noncelen;
+ if (wolfSSL_quic_aead_decrypt(dest, aead_ctx->native_handle, ciphertext,
+ ciphertextlen, nonce, aad,
+ aadlen) != WOLFSSL_SUCCESS) {
+
+ DEBUG_MSG("WOLFSSL: decrypt FAILED\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ WOLFSSL_EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
+ int len;
+
+ (void)hp;
+
+ if (wolfSSL_EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) !=
+ WOLFSSL_SUCCESS ||
+ wolfSSL_EVP_CipherUpdate(actx, dest, &len, PLAINTEXT,
+ sizeof(PLAINTEXT) - 1) != WOLFSSL_SUCCESS ||
+ wolfSSL_EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len) !=
+ WOLFSSL_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ WOLFSSL *ssl = ngtcp2_conn_get_tls_native_handle(conn);
+ WOLFSSL_ENCRYPTION_LEVEL level =
+ ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(crypto_level);
+ int rv;
+ int err;
+
+ DEBUG_MSG("WOLFSSL: read/write crypto data, level=%d len=%lu\n", level,
+ datalen);
+ if (datalen > 0) {
+ rv = wolfSSL_provide_quic_data(ssl, level, data, datalen);
+ if (rv != WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: read/write crypto data FAILED, rv=%d\n", rv);
+ return -1;
+ }
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ rv = wolfSSL_quic_do_handshake(ssl);
+ DEBUG_MSG("WOLFSSL: do_handshake, rv=%d\n", rv);
+ if (rv <= 0) {
+ err = wolfSSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ DEBUG_MSG("WOLFSSL: handshake done\n");
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = wolfSSL_process_quic_post_handshake(ssl);
+ DEBUG_MSG("WOLFSSL: process post handshake, rv=%d\n", rv);
+ if (rv != 1) {
+ err = wolfSSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ WOLFSSL *ssl = tls;
+ const uint8_t *tp;
+ size_t tplen;
+ int rv;
+
+ wolfSSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
+ DEBUG_MSG("WOLFSSL: get peer transport params, len=%lu\n", tplen);
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
+ if (rv != 0) {
+ DEBUG_MSG("WOLFSSL: decode peer transport params failed, rv=%d\n", rv);
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ WOLFSSL *ssl = tls;
+ DEBUG_MSG("WOLFSSL: set local peer transport params, len=%lu\n", len);
+ if (wolfSSL_set_quic_transport_params(ssl, buf, len) != WOLFSSL_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level) {
+ switch (wolfssl_level) {
+ case wolfssl_encryption_initial:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case wolfssl_encryption_early_data:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case wolfssl_encryption_handshake:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case wolfssl_encryption_application:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ abort(); /* if NDEBUG is set */
+ }
+}
+
+WOLFSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return wolfssl_encryption_initial;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return wolfssl_encryption_handshake;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return wolfssl_encryption_application;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return wolfssl_encryption_early_data;
+ default:
+ assert(0);
+ abort(); /* if NDEBUG is set */
+ }
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ DEBUG_MSG("WOLFSSL: get path challenge data\n");
+ if (wolfSSL_RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ DEBUG_MSG("WOLFSSL: get random\n");
+ if (wolfSSL_RAND_bytes(data, (int)datalen) != 1) {
+ return -1;
+ }
+ return 0;
+}
+
+static int set_encryption_secrets(WOLFSSL *ssl,
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level,
+ const uint8_t *rx_secret,
+ const uint8_t *tx_secret, size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level);
+
+ DEBUG_MSG("WOLFSSL: set encryption secrets, level=%d, rxlen=%lu, txlen=%lu\n",
+ wolfssl_level, rx_secret ? secretlen : 0,
+ tx_secret ? secretlen : 0);
+ if (rx_secret &&
+ ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ rx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ if (tx_secret &&
+ ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ tx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int add_handshake_data(WOLFSSL *ssl,
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level);
+ int rv;
+
+ DEBUG_MSG("WOLFSSL: add handshake data, level=%d len=%lu\n", wolfssl_level,
+ datalen);
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int flush_flight(WOLFSSL *ssl) {
+ (void)ssl;
+ return 1;
+}
+
+static int send_alert(WOLFSSL *ssl, enum wolfssl_encryption_level_t level,
+ uint8_t alert) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)level;
+
+ DEBUG_MSG("WOLFSSL: send alert, level=%d alert=%d\n", level, alert);
+ ngtcp2_conn_set_tls_alert(conn, alert);
+
+ return 1;
+}
+
+static WOLFSSL_QUIC_METHOD quic_method = {
+ set_encryption_secrets,
+ add_handshake_data,
+ flush_flight,
+ send_alert,
+};
+
+static void crypto_wolfssl_configure_context(WOLFSSL_CTX *ssl_ctx) {
+ wolfSSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+ wolfSSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+ wolfSSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+}
+
+int ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx) {
+ crypto_wolfssl_configure_context(ssl_ctx);
+ return 0;
+}
+
+int ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx) {
+ crypto_wolfssl_configure_context(ssl_ctx);
+ wolfSSL_CTX_UseSessionTicket(ssl_ctx);
+ return 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
index 8a37bebda6b..ed71cb3ea0c 100644
--- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
+++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
@@ -32,8 +32,9 @@
# define WIN32
#endif
-#ifdef __cplusplus
-extern "C" {
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable : 4324)
#endif
#include <stdlib.h>
@@ -49,10 +50,29 @@ extern "C" {
#include <stdarg.h>
#include <stddef.h>
-#ifdef WIN32
-# include <winsock2.h>
+#ifndef NGTCP2_USE_GENERIC_SOCKADDR
+# ifdef WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <ws2tcpip.h>
+# else
+# include <sys/socket.h>
+# include <netinet/in.h>
+# endif
+#endif
+
+#ifdef AF_INET
+# define NGTCP2_AF_INET AF_INET
#else
-# include <sys/socket.h>
+# define NGTCP2_AF_INET 2
+#endif
+
+#ifdef AF_INET6
+# define NGTCP2_AF_INET6 AF_INET6
+#else
+# define NGTCP2_AF_INET6 23
+# define NGTCP2_USE_GENERIC_IPV6_SOCKADDR
#endif
#include <ngtcp2/version.h>
@@ -73,6 +93,16 @@ extern "C" {
# endif /* !BUILDING_NGTCP2 */
#endif /* !defined(WIN32) */
+#ifdef _MSC_VER
+# define NGTCP2_ALIGN(N) __declspec(align(N))
+#else /* !_MSC_VER */
+# define NGTCP2_ALIGN(N) __attribute__((aligned(N)))
+#endif /* !_MSC_VER */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* @typedef
*
@@ -83,58 +113,69 @@ typedef ptrdiff_t ngtcp2_ssize;
/**
* @functypedef
*
- * Custom memory allocator to replace malloc(). The |mem_user_data|
- * is the mem_user_data member of :type:`ngtcp2_mem` structure.
+ * :type:`ngtcp2_malloc` is a custom memory allocator to replace
+ * :manpage:`malloc(3)`. The |user_data| is
+ * :member:`ngtcp2_mem.user_data`.
*/
-typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data);
+typedef void *(*ngtcp2_malloc)(size_t size, void *user_data);
/**
* @functypedef
*
- * Custom memory allocator to replace free(). The |mem_user_data| is
- * the mem_user_data member of :type:`ngtcp2_mem` structure.
+ * :type:`ngtcp2_free` is a custom memory allocator to replace
+ * :manpage:`free(3)`. The |user_data| is
+ * :member:`ngtcp2_mem.user_data`.
*/
-typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data);
+typedef void (*ngtcp2_free)(void *ptr, void *user_data);
/**
* @functypedef
*
- * Custom memory allocator to replace calloc(). The |mem_user_data|
- * is the mem_user_data member of :type:`ngtcp2_mem` structure.
+ * :type:`ngtcp2_calloc` is a custom memory allocator to replace
+ * :manpage:`calloc(3)`. The |user_data| is the
+ * :member:`ngtcp2_mem.user_data`.
*/
-typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data);
+typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *user_data);
/**
* @functypedef
*
- * Custom memory allocator to replace realloc(). The |mem_user_data|
- * is the mem_user_data member of :type:`ngtcp2_mem` structure.
+ * :type:`ngtcp2_realloc` is a custom memory allocator to replace
+ * :manpage:`realloc(3)`. The |user_data| is the
+ * :member:`ngtcp2_mem.user_data`.
*/
-typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data);
+typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data);
/**
* @struct
*
- * Custom memory allocator functions and user defined pointer. The
- * |mem_user_data| member is passed to each allocator function. This
- * can be used, for example, to achieve per-session memory pool.
+ * :type:`ngtcp2_mem` is a custom memory allocator. The
+ * :member:`user_data` field is passed to each allocator function.
+ * This can be used, for example, to achieve per-connection memory
+ * pool.
*
* In the following example code, ``my_malloc``, ``my_free``,
* ``my_calloc`` and ``my_realloc`` are the replacement of the
- * standard allocators ``malloc``, ``free``, ``calloc`` and
- * ``realloc`` respectively::
+ * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`,
+ * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively::
*
- * void *my_malloc_cb(size_t size, void *mem_user_data) {
+ * void *my_malloc_cb(size_t size, void *user_data) {
+ * (void)user_data;
* return my_malloc(size);
* }
*
- * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }
+ * void my_free_cb(void *ptr, void *user_data) {
+ * (void)user_data;
+ * my_free(ptr);
+ * }
*
- * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {
+ * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) {
+ * (void)user_data;
* return my_calloc(nmemb, size);
* }
*
- * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {
+ * void *my_realloc_cb(void *ptr, size_t size, void *user_data) {
+ * (void)user_data;
* return my_realloc(ptr, size);
* }
*
@@ -147,24 +188,28 @@ typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data);
*/
typedef struct ngtcp2_mem {
/**
- * An arbitrary user supplied data. This is passed to each
- * allocator function.
+ * :member:`user_data` is an arbitrary user supplied data. This
+ * is passed to each allocator function.
*/
- void *mem_user_data;
+ void *user_data;
/**
- * Custom allocator function to replace malloc().
+ * :member:`malloc` is a custom allocator function to replace
+ * :manpage:`malloc(3)`.
*/
ngtcp2_malloc malloc;
/**
- * Custom allocator function to replace free().
+ * :member:`free` is a custom allocator function to replace
+ * :manpage:`free(3)`.
*/
ngtcp2_free free;
/**
- * Custom allocator function to replace calloc().
+ * :member:`calloc` is a custom allocator function to replace
+ * :manpage:`calloc(3)`.
*/
ngtcp2_calloc calloc;
/**
- * Custom allocator function to replace realloc().
+ * :member:`realloc` is a custom allocator function to replace
+ * :manpage:`realloc(3)`.
*/
ngtcp2_realloc realloc;
} ngtcp2_mem;
@@ -180,7 +225,7 @@ typedef struct ngtcp2_mem {
*
* :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 second.
*/
-#define NGTCP2_SECONDS ((uint64_t)1000000000ULL)
+#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL)
/**
* @macro
@@ -188,7 +233,7 @@ typedef struct ngtcp2_mem {
* :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds
* to 1 millisecond.
*/
-#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL)
+#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL)
/**
* @macro
@@ -196,7 +241,7 @@ typedef struct ngtcp2_mem {
* :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds
* to 1 microsecond.
*/
-#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL)
+#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL)
/**
* @macro
@@ -204,7 +249,7 @@ typedef struct ngtcp2_mem {
* :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to
* 1 nanosecond.
*/
-#define NGTCP2_NANOSECONDS ((uint64_t)1ULL)
+#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL)
/**
* @macrosection
@@ -217,7 +262,17 @@ typedef struct ngtcp2_mem {
*
* :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1.
*/
-#define NGTCP2_PROTO_VER_V1 0x00000001u
+#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_V2_DRAFT` is the provisional version
+ * number for QUIC version 2 draft.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_PROTO_VER_V2_DRAFT ((uint32_t)0x709a50c4u)
/**
* @macro
@@ -252,42 +307,34 @@ typedef struct ngtcp2_mem {
#define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_DRAFT_MIN
/**
- * @macrosection
- *
- * IP packet related macros
- */
-
-/**
* @macro
*
- * :macro:`NGTCP2_MAX_PKTLEN_IPV4` is the maximum datagram size of
- * IPv4 packet without PMTUD.
+ * :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved
+ * version.
*/
-#define NGTCP2_MAX_PKTLEN_IPV4 1252
+#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au
+
/**
- * @macro
+ * @macrosection
*
- * :macro:`NGTCP2_MAX_PKTLEN_IPV6` is the maximum datagram size of
- * IPv6 packet without PMTUD.
+ * UDP datagram related macros
*/
-#define NGTCP2_MAX_PKTLEN_IPV6 1232
/**
* @macro
*
- * :macro:`NGTCP2_MIN_INITIAL_PKTLEN` is the minimum datagram size for
- * a packet sent by client which contains its first Initial packet.
+ * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP
+ * datagram payload size that this endpoint transmits.
*/
-#define NGTCP2_MIN_INITIAL_PKTLEN 1200
+#define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200
/**
* @macro
*
- * :macro:`NGTCP2_DEFAULT_MAX_PKTLEN` is the default maximum datagram
- * size that this endpoint transmits. It is used by congestion
- * controller to compute congestion window.
+ * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP
+ * datagram payload size that Path MTU Discovery can discover.
*/
-#define NGTCP2_DEFAULT_MAX_PKTLEN 1200
+#define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452
/**
* @macrosection
@@ -315,13 +362,21 @@ typedef struct ngtcp2_mem {
* @macro
*
* :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` is the minimum length
- * of random bytes (Unpredictable Bits) in Stateless Retry packet
+ * of random bytes (Unpredictable Bits) in Stateless Reset packet
*/
#define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5
/**
* @macro
*
+ * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` is the length of
+ * PATH_CHALLENGE data.
+ */
+#define NGTCP2_PATH_CHALLENGE_DATALEN 8
+
+/**
+ * @macro
+ *
* :macro:`NGTCP2_RETRY_KEY_DRAFT` is an encryption key to create
* integrity tag of Retry packet. It is used for QUIC draft versions.
*/
@@ -357,6 +412,28 @@ typedef struct ngtcp2_mem {
/**
* @macro
*
+ * :macro:`NGTCP2_RETRY_KEY_V2_DRAFT` is an encryption key to create
+ * integrity tag of Retry packet. It is used for QUIC v2 draft.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_RETRY_KEY_V2_DRAFT \
+ "\xba\x85\x8d\xc7\xb4\x3d\xe5\xdb\xf8\x76\x17\xff\x4a\xb2\x53\xdb"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_NONCE_V2_DRAFT` is nonce used when generating
+ * integrity tag of Retry packet. It is used for QUIC v2 draft.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_RETRY_NONCE_V2_DRAFT \
+ "\x14\x1b\x99\xc2\x39\xb0\x3e\x78\x5d\x6a\x2e\x9f"
+
+/**
+ * @macro
+ *
* :macro:`NGTCP2_HP_MASKLEN` is the length of header protection mask.
*/
#define NGTCP2_HP_MASKLEN 5
@@ -400,6 +477,14 @@ typedef struct ngtcp2_mem {
#define NGTCP2_MIN_INITIAL_DCIDLEN 8
/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT` is the default handshake
+ * timeout.
+ */
+#define NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS)
+
+/**
* @macrosection
*
* ECN related macros
@@ -440,15 +525,18 @@ typedef struct ngtcp2_mem {
*/
#define NGTCP2_ECN_MASK 0x3
+#define NGTCP2_PKT_INFO_VERSION_V1 1
+#define NGTCP2_PKT_INFO_VERSION NGTCP2_PKT_INFO_VERSION_V1
+
/**
* @struct
*
* :type:`ngtcp2_pkt_info` is a packet metadata.
*/
-typedef struct ngtcp2_pkt_info {
+typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info {
/**
- * :member:`ecn <ngtcp2_pkt_info.ecn>` is ECN marking and when
- * passing `ngtcp2_conn_read_pkt()`, and it should be either
+ * :member:`ecn` is ECN marking and when passing
+ * `ngtcp2_conn_read_pkt()`, and it should be either
* :macro:`NGTCP2_ECN_NOT_ECT`, :macro:`NGTCP2_ECN_ECT_1`,
* :macro:`NGTCP2_ECN_ECT_0`, or :macro:`NGTCP2_ECN_CE`.
*/
@@ -580,9 +668,9 @@ typedef struct ngtcp2_pkt_info {
/**
* @macro
*
- * :macro:`NGTCP2_ERR_TLS_DECRYPT` indicates TLS decryption failure.
+ * :macro:`NGTCP2_ERR_DECRYPT` indicates a decryption failure.
*/
-#define NGTCP2_ERR_TLS_DECRYPT -220
+#define NGTCP2_ERR_DECRYPT -220
/**
* @macro
*
@@ -641,13 +729,6 @@ typedef struct ngtcp2_pkt_info {
/**
* @macro
*
- * :macro:`NGTCP2_ERR_PATH_VALIDATION_FAILED` indicates that a path
- * validation failed.
- */
-#define NGTCP2_ERR_PATH_VALIDATION_FAILED -236
-/**
- * @macro
- *
* :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` indicates that there is no
* spare Connection ID available.
*/
@@ -699,12 +780,41 @@ typedef struct ngtcp2_pkt_info {
* @macro
*
* :macro:`NGTCP2_ERR_NO_VIABLE_PATH` indicates that path validation
- * could not probe that a path is not capable of at least 1200 MTU.
+ * could not probe that a path is capable of sending UDP datagram
+ * payload of size at least 1200 bytes.
*/
#define NGTCP2_ERR_NO_VIABLE_PATH -244
/**
* @macro
*
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` indicates that server
+ * should send Version Negotiation packet.
+ */
+#define NGTCP2_ERR_VERSION_NEGOTIATION -245
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` indicates that QUIC
+ * connection is not established before the specified deadline.
+ */
+#define NGTCP2_ERR_HANDSHAKE_TIMEOUT -246
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` indicates the
+ * version negotiation failed.
+ */
+#define NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE -247
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_IDLE_CLOSE` indicates the connection should be
+ * closed silently because of idle timeout.
+ */
+#define NGTCP2_ERR_IDLE_CLOSE -248
+/**
+ * @macro
+ *
* :macro:`NGTCP2_ERR_FATAL` indicates that error codes less than this
* value is fatal error. When this error is returned, an endpoint
* should drop connection immediately.
@@ -735,54 +845,68 @@ typedef struct ngtcp2_pkt_info {
*
* :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_PKT_FLAG_NONE 0
+#define NGTCP2_PKT_FLAG_NONE 0x00u
/**
* @macro
*
- * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long packet
+ * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long header packet
* header.
*/
-#define NGTCP2_PKT_FLAG_LONG_FORM 0x01
+#define NGTCP2_PKT_FLAG_LONG_FORM 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR` indicates that Fixed Bit
+ * (aka QUIC bit) is not set.
+ */
+#define NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR 0x02u
/**
* @macro
*
* :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set.
*/
-#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04
+#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04u
/**
* @enum
*
- * :type:`ngtcp2_pkt_type` defines QUIC packet types.
+ * :type:`ngtcp2_pkt_type` defines QUIC version-independent QUIC
+ * packet types.
*/
typedef enum ngtcp2_pkt_type {
/**
* :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` is defined by libngtcp2
* for convenience.
*/
- NGTCP2_PKT_VERSION_NEGOTIATION = 0xf0,
+ NGTCP2_PKT_VERSION_NEGOTIATION = 0x80,
+ /**
+ * :enum:`NGTCP2_PKT_STATELESS_RESET` is defined by libngtcp2 for
+ * convenience.
+ */
+ NGTCP2_PKT_STATELESS_RESET = 0x81,
/**
* :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet.
*/
- NGTCP2_PKT_INITIAL = 0x0,
+ NGTCP2_PKT_INITIAL = 0x10,
/**
* :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet.
*/
- NGTCP2_PKT_0RTT = 0x1,
+ NGTCP2_PKT_0RTT = 0x11,
/**
* :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet.
*/
- NGTCP2_PKT_HANDSHAKE = 0x2,
+ NGTCP2_PKT_HANDSHAKE = 0x12,
/**
* :enum:`NGTCP2_PKT_RETRY` indicates Retry packet.
*/
- NGTCP2_PKT_RETRY = 0x3,
+ NGTCP2_PKT_RETRY = 0x13,
/**
- * :enum:`NGTCP2_PKT_SHORT` is defined by libngtcp2 for convenience.
+ * :enum:`NGTCP2_PKT_1RTT` is defined by libngtcp2 for convenience.
*/
- NGTCP2_PKT_SHORT = 0x70
+ NGTCP2_PKT_1RTT = 0x40
} ngtcp2_pkt_type;
/**
@@ -935,6 +1059,16 @@ typedef enum ngtcp2_pkt_type {
#define NGTCP2_CRYPTO_ERROR 0x100u
/**
+ * @macro
+ *
+ * :macro:`NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT` is QUIC transport
+ * error code ``VERSION_NEGOTIATION_ERROR``.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT 0x53f8u
+
+/**
* @enum
*
* :type:`ngtcp2_path_validation_result` defines path validation
@@ -950,7 +1084,12 @@ typedef enum ngtcp2_path_validation_result {
* :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE` indicates
* validation failure.
*/
- NGTCP2_PATH_VALIDATION_RESULT_FAILURE
+ NGTCP2_PATH_VALIDATION_RESULT_FAILURE,
+ /**
+ * :enum:`NGTCP2_PATH_VALIDATION_RESULT_ABORTED` indicates that path
+ * validation was aborted.
+ */
+ NGTCP2_PATH_VALIDATION_RESULT_ABORTED
} ngtcp2_path_validation_result;
/**
@@ -975,13 +1114,11 @@ typedef uint64_t ngtcp2_duration;
*/
typedef struct ngtcp2_cid {
/**
- * :member:`datalen <ngtcp2_cid.datalen>` is the length of
- * Connection ID.
+ * :member:`datalen` is the length of Connection ID.
*/
size_t datalen;
/**
- * :member:`data <ngtcp2_cid.data>` is the buffer to store
- * Connection ID.
+ * :member:`data` is the buffer to store Connection ID.
*/
uint8_t data[NGTCP2_MAX_CIDLEN];
} ngtcp2_cid;
@@ -994,12 +1131,12 @@ typedef struct ngtcp2_cid {
*/
typedef struct ngtcp2_vec {
/**
- * :member:`base <ngtcp2_vec.base>` points to the data.
+ * :member:`base` points to the data.
*/
uint8_t *base;
/**
- * :member:`len <ngtcp2_vec.len>` is the number of bytes which the
- * buffer pointed by base contains.
+ * :member:`len` is the number of bytes which the buffer pointed by
+ * base contains.
*/
size_t len;
} ngtcp2_vec;
@@ -1009,13 +1146,20 @@ typedef struct ngtcp2_vec {
*
* `ngtcp2_cid_init` initializes Connection ID |cid| with the byte
* string pointed by |data| and its length is |datalen|. |datalen|
- * must be at least :macro:`NGTCP2_MIN_CIDLEN`, and at most
- * :macro:`NGTCP2_MAX_CIDLEN`.
+ * must be at most :macro:`NGTCP2_MAX_CIDLEN`.
*/
NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data,
size_t datalen);
/**
+ * @function
+ *
+ * `ngtcp2_cid_eq` returns nonzero if |a| and |b| share the same
+ * Connection ID.
+ */
+NGTCP2_EXTERN int ngtcp2_cid_eq(const ngtcp2_cid *a, const ngtcp2_cid *b);
+
+/**
* @struct
*
* :type:`ngtcp2_pkt_hd` represents QUIC packet header.
@@ -1058,8 +1202,8 @@ typedef struct ngtcp2_pkt_hd {
*/
uint8_t type;
/**
- * :member:`flags` is zero or more of NGTCP2_PKT_FLAG_*. See
- * :macro:`NGTCP2_PKT_FLAG_NONE`.
+ * :member:`flags` is zero or more of :macro:`NGTCP2_PKT_FLAG_*
+ * <NGTCP2_PKT_FLAG_NONE>`.
*/
uint8_t flags;
} ngtcp2_pkt_hd;
@@ -1085,27 +1229,6 @@ typedef struct ngtcp2_pkt_stateless_reset {
size_t randlen;
} ngtcp2_pkt_stateless_reset;
-typedef enum ngtcp2_transport_param_id {
- NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000,
- NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001,
- NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002,
- NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003,
- NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004,
- NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005,
- NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006,
- NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007,
- NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008,
- NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009,
- NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a,
- NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b,
- NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c,
- NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d,
- NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e,
- NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f,
- NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010,
- NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020
-} ngtcp2_transport_param_id;
-
/**
* @enum
*
@@ -1126,20 +1249,6 @@ typedef enum ngtcp2_transport_params_type {
} ngtcp2_transport_params_type;
/**
- * @enum
- *
- * ngtcp2_rand_usage describes the usage of the generated random data.
- */
-typedef enum ngtcp2_rand_usage {
- NGTCP2_RAND_USAGE_NONE,
- /**
- * :enum:`NGTCP2_RAND_USAGE_PATH_CHALLENGE` indicates that random
- * value is used for PATH_CHALLENGE.
- */
- NGTCP2_RAND_USAGE_PATH_CHALLENGE
-} ngtcp2_rand_usage;
-
-/**
* @macrosection
*
* QUIC transport parameters related macros
@@ -1148,10 +1257,10 @@ typedef enum ngtcp2_rand_usage {
/**
* @macro
*
- * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` is the default value
- * of max_udp_payload_size transport parameter.
+ * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` is the default
+ * value of max_udp_payload_size transport parameter.
*/
-#define NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE 65527
+#define NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE 65527
/**
* @macro
@@ -1182,10 +1291,19 @@ typedef enum ngtcp2_rand_usage {
/**
* @macro
*
- * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS` is TLS extension
- * type of quic_transport_parameters.
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1` is TLS
+ * extension type of quic_transport_parameters.
*/
-#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS 0xffa5u
+#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1 0x39u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT` is TLS
+ * extension type of quic_transport_parameters used during draft
+ * development.
+ */
+#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT 0xffa5u
/**
* @struct
@@ -1215,6 +1333,16 @@ typedef struct ngtcp2_preferred_addr {
*/
uint8_t ipv6_addr[16];
/**
+ * :member:`ipv4_present` indicates that :member:`ipv4_addr` and
+ * :member:`ipv4_port` contain IPv4 address and port respectively.
+ */
+ uint8_t ipv4_present;
+ /**
+ * :member:`ipv6_present` indicates that :member:`ipv6_addr` and
+ * :member:`ipv6_port` contain IPv6 address and port respectively.
+ */
+ uint8_t ipv6_present;
+ /**
* :member:`stateless_reset_token` contains stateless reset token.
*/
uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
@@ -1223,6 +1351,32 @@ typedef struct ngtcp2_preferred_addr {
/**
* @struct
*
+ * :type:`ngtcp2_version_info` represents version_information
+ * structure.
+ */
+typedef struct ngtcp2_version_info {
+ /**
+ * :member:`chosen_version` is the version chosen by the sender.
+ */
+ uint32_t chosen_version;
+ /**
+ * :member:`other_versions` points the wire image of other_versions
+ * field. The each version is therefore in network byte order.
+ */
+ uint8_t *other_versions;
+ /**
+ * :member:`other_versionslen` is the number of bytes pointed by
+ * :member:`other_versions`, not the number of versions included.
+ */
+ size_t other_versionslen;
+} ngtcp2_version_info;
+
+#define NGTCP2_TRANSPORT_PARAMS_VERSION_V1 1
+#define NGTCP2_TRANSPORT_PARAMS_VERSION NGTCP2_TRANSPORT_PARAMS_VERSION_V1
+
+/**
+ * @struct
+ *
* :type:`ngtcp2_transport_params` represents QUIC transport
* parameters.
*/
@@ -1324,7 +1478,7 @@ typedef struct ngtcp2_transport_params {
* :member:`max_datagram_frame_size` is the maximum size of DATAGRAM
* frame that this endpoint willingly receives. Specifying 0
* disables DATAGRAM support. See
- * https://tools.ietf.org/html/draft-ietf-quic-datagram-01
+ * https://datatracker.ietf.org/doc/html/rfc9221
*/
uint64_t max_datagram_frame_size;
/**
@@ -1351,16 +1505,29 @@ typedef struct ngtcp2_transport_params {
* :member:`stateless_reset_token` contains stateless reset token.
*/
uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ /**
+ * :member:`grease_quic_bit` is nonzero if sender supports "Greasing
+ * the QUIC Bit" extension. See
+ * https://datatracker.ietf.org/doc/html/draft-ietf-quic-bit-grease.
+ * Note that the local endpoint always enables greasing QUIC bit
+ * regardless of this field value.
+ */
+ uint8_t grease_quic_bit;
+ /**
+ * :member:`version_info` contains version_information field if
+ * :member:`version_info_present` is nonzero. Application should
+ * not specify this field.
+ */
+ ngtcp2_version_info version_info;
+ /**
+ * :member:`version_info_present` is nonzero if
+ * :member:`version_info` is set. Application should not specify
+ * this field.
+ */
+ uint8_t version_info_present;
} ngtcp2_transport_params;
/**
- * @struct
- *
- * :type:`ngtcp2_log` is ngtcp2 library internal logger.
- */
-typedef struct ngtcp2_log ngtcp2_log;
-
-/**
* @enum
*
* :type:`ngtcp2_pktns_id` defines packet number space identifier.
@@ -1388,6 +1555,9 @@ typedef enum ngtcp2_pktns_id {
NGTCP2_PKTNS_ID_MAX
} ngtcp2_pktns_id;
+#define NGTCP2_CONN_STAT_VERSION_V1 1
+#define NGTCP2_CONN_STAT_VERSION NGTCP2_CONN_STAT_VERSION_V1
+
/**
* @struct
*
@@ -1435,13 +1605,11 @@ typedef struct ngtcp2_conn_stat {
ngtcp2_tstamp loss_detection_timer;
/**
* :member:`last_tx_pkt_ts` corresponds to
- * time_of_last_sent_ack_eliciting_packet in
- * draft-ietf-quic-recovery-32.
+ * time_of_last_ack_eliciting_packet in :rfc:`9002`.
*/
ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX];
/**
- * :member:`loss_time` corresponds to loss_time in
- * draft-ietf-quic-recovery-32.
+ * :member:`loss_time` corresponds to loss_time in :rfc:`9002`.
*/
ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX];
/**
@@ -1473,6 +1641,16 @@ typedef struct ngtcp2_conn_stat {
* in byte per second.
*/
uint64_t delivery_rate_sec;
+ /**
+ * :member:`pacing_rate` is the current packet sending rate. If
+ * pacing is disabled, 0 is set.
+ */
+ double pacing_rate;
+ /**
+ * :member:`send_quantum` is the maximum size of a data aggregate
+ * scheduled and transmitted together.
+ */
+ size_t send_quantum;
} ngtcp2_conn_stat;
/**
@@ -1490,195 +1668,16 @@ typedef enum ngtcp2_cc_algo {
*/
NGTCP2_CC_ALGO_CUBIC = 0x01,
/**
- * :enum:`NGTCP2_CC_ALGO_CUSTOM` represents custom congestion
- * control algorithm.
- */
- NGTCP2_CC_ALGO_CUSTOM = 0xff
-} ngtcp2_cc_algo;
-
-/**
- * @struct
- *
- * :type:`ngtcp2_cc_base` is the base structure of custom congestion
- * control algorithm. It must be the first field of custom congestion
- * controller.
- */
-typedef struct ngtcp2_cc_base {
- /**
- * :member:`log` is ngtcp2 library internal logger.
- */
- ngtcp2_log *log;
-} ngtcp2_cc_base;
-
-/**
- * @struct
- *
- * :type:`ngtcp2_cc_pkt` is a convenient structure to include
- * acked/lost/sent packet.
- */
-typedef struct ngtcp2_cc_pkt {
- /**
- * :member:`pkt_num` is the packet number
- */
- int64_t pkt_num;
- /**
- * :member:`pktlen` is the length of packet.
- */
- size_t pktlen;
- /**
- * :member:`pktns_id` is the ID of packet number space which this
- * packet belongs to.
- */
- ngtcp2_pktns_id pktns_id;
- /**
- * :member:`ts_sent` is the timestamp when packet is sent.
- */
- ngtcp2_tstamp ts_sent;
-} ngtcp2_cc_pkt;
-
-typedef struct ngtcp2_cc ngtcp2_cc;
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is
- * called with an acknowledged packet.
- */
-typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_pkt *pkt,
- ngtcp2_tstamp ts);
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_congestion_event` is a callback function which is
- * called when congestion event happens (e.g., when packet is lost).
- */
-typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc,
- ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts_sent,
- ngtcp2_tstamp ts);
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function
- * which is called when persistent congestion is established.
- */
-typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc,
- ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is
- * called when an acknowledgement is received.
- */
-typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is
- * called when an ack-eliciting packet is sent.
- */
-typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_pkt *pkt);
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is
- * called when new RTT sample is obtained.
- */
-typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_reset` is a callback function which is called when
- * congestion state must be reset.
- */
-typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc);
-
-/**
- * @enum
- *
- * :type:`ngtcp2_cc_event_type` defines congestion control events.
- */
-typedef enum ngtcp2_cc_event_type {
- /**
- * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet
- * is sent and no other ack-eliciting packet is present.
- */
- NGTCP2_CC_EVENT_TYPE_TX_START
-} ngtcp2_cc_event_type;
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_event` is a callback function which is called when
- * a specific event happens.
- */
-typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
-
-/**
- * @struct
- *
- * :type:`ngtcp2_cc` is congestion control algorithm interface to
- * allow custom implementation.
- */
-typedef struct ngtcp2_cc {
- /**
- * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which
- * usually contains a state.
- */
- ngtcp2_cc_base *ccb;
- /**
- * :member:`on_pkt_acked` is a callback function which is called
- * when a packet is acknowledged.
- */
- ngtcp2_cc_on_pkt_acked on_pkt_acked;
- /**
- * :member:`congestion_event` is a callback function which is called
- * when congestion event happens (.e.g, packet is lost).
+ * :enum:`NGTCP2_CC_ALGO_BBR` represents BBR. If BBR is chosen,
+ * packet pacing is enabled.
*/
- ngtcp2_cc_congestion_event congestion_event;
+ NGTCP2_CC_ALGO_BBR = 0x02,
/**
- * :member:`on_persistent_congestion` is a callback function which
- * is called when persistent congestion is established.
+ * :enum:`NGTCP2_CC_ALGO_BBR2` represents BBR v2. If BBR v2 is
+ * chosen, packet pacing is enabled.
*/
- ngtcp2_cc_on_persistent_congestion on_persistent_congestion;
- /**
- * :member:`on_ack_recv` is a callback function which is called when
- * an acknowledgement is received.
- */
- ngtcp2_cc_on_ack_recv on_ack_recv;
- /**
- * :member:`on_pkt_sent` is a callback function which is called when
- * ack-eliciting packet is sent.
- */
- ngtcp2_cc_on_pkt_sent on_pkt_sent;
- /**
- * :member:`new_rtt_sample` is a callback function which is called
- * when new RTT sample is obtained.
- */
- ngtcp2_cc_new_rtt_sample new_rtt_sample;
- /**
- * :member:`reset` is a callback function which is called when
- * congestion control state must be reset.
- */
- ngtcp2_cc_reset reset;
- /**
- * :member:`event` is a callback function which is called when a
- * specific event happens.
- */
- ngtcp2_cc_event event;
-} ngtcp2_cc;
+ NGTCP2_CC_ALGO_BBR2 = 0x03
+} ngtcp2_cc_algo;
/**
* @functypedef
@@ -1700,14 +1699,14 @@ typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...);
*
* :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_QLOG_WRITE_FLAG_NONE 0
+#define NGTCP2_QLOG_WRITE_FLAG_NONE 0x00u
/**
* @macro
*
* :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` indicates that this is the
* final call to :type:`ngtcp2_qlog_write` in the current connection.
*/
-#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01
+#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01u
/**
* @struct
@@ -1730,8 +1729,8 @@ typedef struct ngtcp2_rand_ctx {
*
* :type:`ngtcp2_qlog_write` is a callback function which is called to
* write qlog |data| of length |datalen| bytes. |flags| is bitwise OR
- * of zero or more of NGTCP2_QLOG_WRITE_FLAG_*. See
- * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE`. If
+ * of zero or more of :macro:`NGTCP2_QLOG_WRITE_FLAG_*
+ * <NGTCP2_QLOG_WRITE_FLAG_NONE>`. If
* :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0.
*/
typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags,
@@ -1757,6 +1756,9 @@ typedef struct ngtcp2_qlog_settings {
ngtcp2_qlog_write write;
} ngtcp2_qlog_settings;
+#define NGTCP2_SETTINGS_VERSION_V1 1
+#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_VERSION_V1
+
/**
* @struct
*
@@ -1768,19 +1770,10 @@ typedef struct ngtcp2_settings {
*/
ngtcp2_qlog_settings qlog;
/**
- * :member:`cc_algo` specifies congestion control algorithm. If
- * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` is set, :member:`cc`
- * must be set to a pointer to custom congestion control algorithm.
+ * :member:`cc_algo` specifies congestion control algorithm.
*/
ngtcp2_cc_algo cc_algo;
/**
- * :member:`cc` is a pointer to custom congestion control algorithm.
- * :member:`cc_algo` must be set to
- * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` in order to enable
- * custom congestion control algorithm.
- */
- ngtcp2_cc *cc;
- /**
* :member:`initial_ts` is an initial timestamp given to the
* library.
*/
@@ -1798,8 +1791,7 @@ typedef struct ngtcp2_settings {
/**
* :member:`max_udp_payload_size` is the maximum size of UDP
* datagram payload that this endpoint transmits. It is used by
- * congestion controller to compute congestion window. If it is set
- * to 0, it defaults to :macro:`NGTCP2_DEFAULT_MAX_PKTLEN`.
+ * congestion controller to compute congestion window.
*/
size_t max_udp_payload_size;
/**
@@ -1847,8 +1839,173 @@ typedef struct ngtcp2_settings {
* immediate acknowledgement.
*/
size_t ack_thresh;
+ /**
+ * :member:`no_udp_payload_size_shaping`, if set to nonzero,
+ * instructs the library not to limit the UDP payload size to
+ * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended by
+ * Path MTU Discovery) and instead use the mininum size among the
+ * given buffer size, :member:`max_udp_payload_size`, and the
+ * received max_udp_payload QUIC transport parameter.
+ */
+ int no_udp_payload_size_shaping;
+ /**
+ * :member:`handshake_timeout` is the period of time before giving
+ * up QUIC connection establishment. If QUIC handshake is not
+ * complete within this period, `ngtcp2_conn_handle_expiry` returns
+ * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` error. The deadline is
+ * :member:`initial_ts` + :member:`handshake_timeout`. If this
+ * field is set to ``UINT64_MAX``, no handshake timeout is set.
+ */
+ ngtcp2_duration handshake_timeout;
+ /**
+ * :member:`preferred_versions` is the array of versions that are
+ * preferred by the local endpoint. All versions set in this array
+ * must be supported by the library, and compatible to QUIC v1. The
+ * reserved versions are not allowed. They are sorted in the order
+ * of preference.
+ *
+ * On compatible version negotiation, server will negotiate one of
+ * those versions contained in this array if a client initially
+ * chooses a less preferred version. This version set corresponds
+ * to Offered Versions in QUIC Version Negotiation draft, and it should
+ * be sent in Version Negotiation packet.
+ *
+ * Client uses this field and :member:`original_version` to prevent
+ * version downgrade attack if it reacted upon Version Negotiation
+ * packet. If this field is specified, client must include
+ * |client_chosen_version| passed to `ngtcp2_conn_client_new` unless
+ * |client_chosen_version| is a reserved version.
+ */
+ uint32_t *preferred_versions;
+ /**
+ * :member:`preferred_versionslen` is the number of versions that
+ * are contained in the array pointed by
+ * :member:`preferred_versions`.
+ */
+ size_t preferred_versionslen;
+ /**
+ * :member:`other_versions` is the array of versions that are set in
+ * :member:`other_versions <ngtcp2_version_info.other_versions>`
+ * field of outgoing version_information QUIC transport parameter.
+ *
+ * For server, this corresponds to Fully-Deployed Versions in QUIC
+ * Version Negotiation draft. If this field is set not, it is set
+ * to :member:`preferred_versions` internally if
+ * :member:`preferred_versionslen` is not zero. If this field is
+ * not set, and :member:`preferred_versionslen` is zero, this field
+ * is set to :macro:`NGTCP2_PROTO_VER_V1` internally.
+ *
+ * Client must include |client_chosen_version| passed to
+ * `ngtcp2_conn_client_new` in this array if this field is set and
+ * |client_chosen_version| is not a reserved version. If this field
+ * is not set, |client_chosen_version| passed to
+ * `ngtcp2_conn_client_new` will be set in this field internally
+ * unless |client_chosen_version| is a reserved version.
+ */
+ uint32_t *other_versions;
+ /**
+ * :member:`other_versionslen` is the number of versions that are
+ * contained in the array pointed by :member:`other_versions`.
+ */
+ size_t other_versionslen;
+ /**
+ * :member:`original_version` is the original version that client
+ * initially used to make a connection attempt. If it is set, and
+ * it differs from |client_chosen_version| passed to
+ * `ngtcp2_conn_client_new`, the library assumes that client reacted
+ * upon Version Negotiation packet. Server does not use this field.
+ */
+ uint32_t original_version;
+ /**
+ * :member:`no_pmtud`, if set to nonzero, disables Path MTU
+ * Discovery.
+ */
+ int no_pmtud;
} ngtcp2_settings;
+#ifdef NGTCP2_USE_GENERIC_SOCKADDR
+typedef struct ngtcp2_sockaddr {
+ uint16_t sa_family;
+ uint8_t sa_data[14];
+} ngtcp2_sockaddr;
+
+typedef struct ngtcp2_in_addr {
+ uint32_t s_addr;
+} ngtcp2_in_addr;
+
+typedef struct ngtcp2_sockaddr_in {
+ uint16_t sin_family;
+ uint16_t sin_port;
+ ngtcp2_in_addr sin_addr;
+ uint8_t sin_zero[8];
+} ngtcp2_sockaddr_in;
+
+# define NGTCP2_SS_MAXSIZE 128
+# define NGTCP2_SS_ALIGNSIZE (sizeof(uint64_t))
+# define NGTCP2_SS_PAD1SIZE (NGTCP2_SS_ALIGNSIZE - sizeof(uint16_t))
+# define NGTCP2_SS_PAD2SIZE \
+ (NGTCP2_SS_MAXSIZE - \
+ (sizeof(uint16_t) + NGTCP2_SS_PAD1SIZE + NGTCP2_SS_ALIGNSIZE))
+
+typedef struct ngtcp2_sockaddr_storage {
+ uint16_t ss_family;
+ uint8_t _ss_pad1[NGTCP2_SS_PAD1SIZE];
+ uint64_t _ss_align;
+ uint8_t _ss_pad2[NGTCP2_SS_PAD2SIZE];
+} ngtcp2_sockaddr_storage;
+
+# undef NGTCP2_SS_PAD2SIZE
+# undef NGTCP2_SS_PAD1SIZE
+# undef NGTCP2_SS_ALIGNSIZE
+# undef NGTCP2_SS_MAXSIZE
+
+typedef uint32_t ngtcp2_socklen;
+#else
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr` is typedefed to struct sockaddr. If
+ * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to
+ * the generic struct sockaddr defined in ngtcp2.h.
+ */
+typedef struct sockaddr ngtcp2_sockaddr;
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr_storage` is typedefed to struct
+ * sockaddr_storage. If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is
+ * defined, it is typedefed to the generic struct sockaddr_storage
+ * defined in ngtcp2.h.
+ */
+typedef struct sockaddr_storage ngtcp2_sockaddr_storage;
+typedef struct sockaddr_in ngtcp2_sockaddr_in;
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_socklen` is typedefed to socklen_t. If
+ * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to
+ * uint32_t.
+ */
+typedef socklen_t ngtcp2_socklen;
+#endif
+
+#if defined(NGTCP2_USE_GENERIC_SOCKADDR) || \
+ defined(NGTCP2_USE_GENERIC_IPV6_SOCKADDR)
+typedef struct ngtcp2_in6_addr {
+ uint8_t in6_addr[16];
+} ngtcp2_in6_addr;
+
+typedef struct ngtcp2_sockaddr_in6 {
+ uint16_t sin6_family;
+ uint16_t sin6_port;
+ uint32_t sin6_flowinfo;
+ ngtcp2_in6_addr sin6_addr;
+ uint32_t sin6_scope_id;
+} ngtcp2_sockaddr_in6;
+#else
+typedef struct sockaddr_in6 ngtcp2_sockaddr_in6;
+#endif
+
/**
* @struct
*
@@ -1856,19 +2013,14 @@ typedef struct ngtcp2_settings {
*/
typedef struct ngtcp2_addr {
/**
- * :member:`addrlen` is the length of addr.
- */
- size_t addrlen;
- /**
* :member:`addr` points to the buffer which contains endpoint
* address. It must not be ``NULL``.
*/
- struct sockaddr *addr;
+ ngtcp2_sockaddr *addr;
/**
- * :member:`user_data` is an arbitrary data and opaque to the
- * library.
+ * :member:`addrlen` is the length of addr.
*/
- void *user_data;
+ ngtcp2_socklen addrlen;
} ngtcp2_addr;
/**
@@ -1886,6 +2038,21 @@ typedef struct ngtcp2_path {
* :member:`remote` is the address of remote endpoint.
*/
ngtcp2_addr remote;
+ /**
+ * :member:`user_data` is an arbitrary data and opaque to the
+ * library.
+ *
+ * Note that :type:`ngtcp2_path` is generally passed to
+ * :type:`ngtcp2_conn` by an application, and :type:`ngtcp2_conn`
+ * stores their copies. Unfortunately, there is no way for the
+ * application to know when :type:`ngtcp2_conn` finishes using a
+ * specific :type:`ngtcp2_path` object in mid connection, which
+ * means that the application cannot free the data pointed by this
+ * field. Therefore, it is advised to use this field only when the
+ * data pointed by this field persists in an entire lifetime of the
+ * connection.
+ */
+ void *user_data;
} ngtcp2_path;
/**
@@ -1896,17 +2063,17 @@ typedef struct ngtcp2_path {
*/
typedef struct ngtcp2_path_storage {
/**
- * :member:`local_addrbuf` is a buffer to store local address.
+ * :member:`path` stores network path.
*/
- struct sockaddr_storage local_addrbuf;
+ ngtcp2_path path;
/**
- * :member:`remote_addrbuf` is a buffer to store remote address.
+ * :member:`local_addrbuf` is a buffer to store local address.
*/
- struct sockaddr_storage remote_addrbuf;
+ ngtcp2_sockaddr_storage local_addrbuf;
/**
- * :member:`path` stores network path.
+ * :member:`remote_addrbuf` is a buffer to store remote address.
*/
- ngtcp2_path path;
+ ngtcp2_sockaddr_storage remote_addrbuf;
} ngtcp2_path_storage;
/**
@@ -1993,7 +2160,7 @@ typedef struct ngtcp2_crypto_cipher_ctx {
* :type:`ngtcp2_crypto_ctx` is a convenient structure to bind all
* crypto related objects in one place. Use
* `ngtcp2_crypto_ctx_initial` to initialize this struct for Initial
- * packet encryption. For Handshake and Short packets, use
+ * packet encryption. For Handshake and 1RTT packets, use
* `ngtcp2_crypto_ctx_tls`.
*/
typedef struct ngtcp2_crypto_ctx {
@@ -2027,17 +2194,21 @@ typedef struct ngtcp2_crypto_ctx {
* `ngtcp2_encode_transport_params` encodes |params| in |dest| of
* length |destlen|.
*
+ * If |dest| is NULL, and |destlen| is zero, this function just
+ * returns the number of bytes required to store the encoded transport
+ * parameters.
+ *
* This function returns the number of written, or one of the
* following negative error codes:
*
* :macro:`NGTCP2_ERR_NOBUF`
* Buffer is too small.
- * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`:
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
* |exttype| is invalid.
*/
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params(
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype,
- const ngtcp2_transport_params *params);
+ int transport_params_version, const ngtcp2_transport_params *params);
/**
* @function
@@ -2049,6 +2220,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params(
* If the optional parameters are missing, the default value is
* assigned.
*
+ * The following fields may point to somewhere inside the buffer
+ * pointed by |data| of length |datalen|:
+ *
+ * - :member:`ngtcp2_transport_params.version_info.other_versions
+ * <ngtcp2_version_info.other_versions>`
+ *
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
@@ -2056,13 +2233,90 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params(
* The required parameter is missing.
* :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
* The input is malformed.
- * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`:
- * |exttype| is invalid.
*/
-NGTCP2_EXTERN int
-ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
- ngtcp2_transport_params_type exttype,
- const uint8_t *data, size_t datalen);
+NGTCP2_EXTERN int ngtcp2_decode_transport_params_versioned(
+ int transport_params_version, ngtcp2_transport_params *params,
+ ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_decode_transport_params_new` decodes transport parameters
+ * in |data| of length |datalen|, and stores the result in the object
+ * allocated dynamically. The pointer to the allocated object is
+ * assigned to |*pparams|. Unlike `ngtcp2_decode_transport_params`,
+ * all direct and indirect fields are also allocated dynamically if
+ * needed.
+ *
+ * |mem| is a memory allocator to allocate memory. If |mem| is
+ * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()`
+ * is used.
+ *
+ * If the optional parameters are missing, the default value is
+ * assigned.
+ *
+ * `ngtcp2_transport_params_del` frees the memory allocated by this
+ * function.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * The required parameter is missing.
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * The input is malformed.
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_decode_transport_params_new(
+ ngtcp2_transport_params **pparams, ngtcp2_transport_params_type exttype,
+ const uint8_t *data, size_t datalen, const ngtcp2_mem *mem);
+
+/**
+ * @function
+ *
+ * `ngtcp2_transport_params_del` frees the |params| which must be
+ * dynamically allocated by `ngtcp2_decode_transport_params_new`.
+ *
+ * |mem| is a memory allocator that allocated |params|. If |mem| is
+ * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()`
+ * is used.
+ *
+ * If |params| is ``NULL``, this function does nothing.
+ */
+NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_version_cid` is a convenient struct to store the
+ * result of `ngtcp2_pkt_decode_version_cid`.
+ */
+typedef struct ngtcp2_version_cid {
+ /**
+ * :member:`version` stores QUIC version.
+ */
+ uint32_t version;
+ /**
+ * :member:`dcid` points to the Destination Connection ID.
+ */
+ const uint8_t *dcid;
+ /**
+ * :member:`dcidlen` is the length of the Destination Connection ID
+ * pointed by :member:`dcid`.
+ */
+ size_t dcidlen;
+ /**
+ * :member:`scid` points to the Source Connection ID.
+ */
+ const uint8_t *scid;
+ /**
+ * :member:`scidlen` is the length of the Source Connection ID
+ * pointed by :member:`scid`.
+ */
+ size_t scidlen;
+} ngtcp2_version_cid;
/**
* @function
@@ -2076,34 +2330,44 @@ ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
* Longer Connection ID is only valid if the version is unsupported
* QUIC version.
*
- * If the given packet is Long packet, this function extracts the
- * version from the packet and assigns it to |*pversion|. It also
+ * If the given packet is Long header packet, this function extracts
+ * the version from the packet and assigns it to
+ * :member:`dest->version <ngtcp2_version_cid.version>`. It also
* extracts the pointer to the Destination Connection ID and its
- * length and assigns them to |*pdcid| and |*pdcidlen| respectively.
- * Similarly, it extracts the pointer to the Source Connection ID and
- * its length and assigns them to |*pscid| and |*pscidlen|
- * respectively.
- *
- * If the given packet is Short packet, |*pversion| will be 0,
- * |*pscid| will be ``NULL``, and |*pscidlen| will be 0. Because the
- * Short packet does not have the length of Destination Connection ID,
- * the caller has to pass the length in |short_dcidlen|. This
- * function extracts the pointer to the Destination Connection ID and
- * assigns it to |*pdcid|. |short_dcidlen| is assigned to
- * |*pdcidlen|.
- *
- * This function returns 0 or 1 if it succeeds. It returns 1 if
- * Version Negotiation packet should be sent. Otherwise, one of the
+ * length and assigns them to :member:`dest->dcid
+ * <ngtcp2_version_cid.dcid>` and :member:`dest->dcidlen
+ * <ngtcp2_version_cid.dcidlen>` respectively. Similarly, it extracts
+ * the pointer to the Source Connection ID and its length and assigns
+ * them to :member:`dest->scid <ngtcp2_version_cid.scid>` and
+ * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` respectively.
+ *
+ * If the given packet is Short header packet, :member:`dest->version
+ * <ngtcp2_version_cid.version>` will be 0, :member:`dest->scid
+ * <ngtcp2_version_cid.scid>` will be ``NULL``, and
+ * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` will be 0.
+ * Because the Short header packet does not have the length of
+ * Destination Connection ID, the caller has to pass the length in
+ * |short_dcidlen|. This function extracts the pointer to the
+ * Destination Connection ID and assigns it to :member:`dest->dcid
+ * <ngtcp2_version_cid.dcid>`. |short_dcidlen| is assigned to
+ * :member:`dest->dcidlen <ngtcp2_version_cid.dcidlen>`.
+ *
+ * If Version Negotiation is required, this function returns
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`. Unlike the other error
+ * cases, all fields of |dest| are assigned as described above.
+ *
+ * This function returns 0 if it succeeds. Otherwise, one of the
* following negative error code:
*
* :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
* The function could not decode the packet header.
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`
+ * Version Negotiation packet should be sent.
*/
-NGTCP2_EXTERN int
-ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
- size_t *pdcidlen, const uint8_t **pscid,
- size_t *pscidlen, const uint8_t *data,
- size_t datalen, size_t short_dcidlen);
+NGTCP2_EXTERN int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest,
+ const uint8_t *data,
+ size_t datalen,
+ size_t short_dcidlen);
/**
* @function
@@ -2124,8 +2388,11 @@ ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
* Negotiation packet has random type in wire format. For
* convenience, this function sets
* :enum:`ngtcp2_pkt_type.NGTCP2_PKT_VERSION_NEGOTIATION` to
- * dest->type, and set dest->payloadlen and dest->pkt_num to 0.
- * Version Negotiation packet occupies a single packet.
+ * :member:`dest->type <ngtcp2_pkt_hd.type>`, clears
+ * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags
+ * <ngtcp2_pkt_hd.flags>`, and sets 0 to :member:`dest->len
+ * <ngtcp2_pkt_hd.len>`. Version Negotiation packet occupies a single
+ * packet.
*
* It stores the result in the object pointed by |dest|, and returns
* the number of bytes decoded to read the packet header if it
@@ -2141,12 +2408,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest,
/**
* @function
*
- * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in
- * |pkt| of length |pktlen|. |dcidlen| is the length of DCID in
- * packet header. Short packet does not encode the length of
- * connection ID, thus we need the input from the outside. This
- * function only parses the input just before packet number field.
- * This function can handle Connection ID up to
+ * `ngtcp2_pkt_decode_hd_short` decodes QUIC short header packet
+ * header in |pkt| of length |pktlen|. |dcidlen| is the length of
+ * DCID in packet header. Short header packet does not encode the
+ * length of connection ID, thus we need the input from the outside.
+ * This function only parses the input just before packet number
+ * field. This function can handle Connection ID up to
* :macro:`NGTCP2_MAX_CIDLEN`. Consider to use
* `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. It
* stores the result in the object pointed by |dest|, and returns the
@@ -2170,7 +2437,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest,
* and its length must be :macro:`NGTCP2_STATELESS_RESET_TOKENLEN`
* bytes long. |rand| specifies the random octets preceding Stateless
* Reset Token. The length of |rand| is specified by |randlen| which
- * must be at least :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN` bytes
+ * must be at least :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` bytes
* long.
*
* If |randlen| is too long to write them all in the buffer, |rand| is
@@ -2183,7 +2450,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest,
* Buffer is too small.
* :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
* |randlen| is strictly less than
- * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`.
+ * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN`.
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset(
uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token,
@@ -2231,15 +2498,12 @@ typedef struct ngtcp2_conn ngtcp2_conn;
* `ngtcp2_conn_submit_crypto_data` function. Make sure that before
* calling `ngtcp2_conn_submit_crypto_data` function, client
* application must create initial packet protection keys and IVs, and
- * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key`
- * and
+ * provide them to ngtcp2 library using
+ * `ngtcp2_conn_install_initial_key`.
*
* This callback function must return 0 if it succeeds, or
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
* return immediately.
- *
- * TODO: Define error code for TLS stack failure. Suggestion:
- * NGTCP2_ERR_CRYPTO.
*/
typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data);
@@ -2250,16 +2514,13 @@ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data);
* Initial packet from client. An server application must implement
* this callback, and generate initial keys and IVs for both
* transmission and reception. Install them using
- * `ngtcp2_conn_set_initial_key`. |dcid| is the destination
+ * `ngtcp2_conn_install_initial_key`. |dcid| is the destination
* connection ID which client generated randomly. It is used to
* derive initial packet protection keys.
*
* The callback function must return 0 if it succeeds. If an error
* occurs, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the
* library call return immediately.
- *
- * TODO: Define error code for TLS stack failure. Suggestion:
- * NGTCP2_ERR_CRYPTO.
*/
typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn,
const ngtcp2_cid *dcid,
@@ -2310,12 +2571,24 @@ typedef enum ngtcp2_crypto_level {
*
* The application should provide the given data to TLS stack.
*
- * The callback function must return 0 if it succeeds. If TLS stack
- * reported error, return :macro:`NGTCP2_ERR_CRYPTO`. If application
- * encounters fatal error, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
- * which makes the library call return immediately. If the other
- * value is returned, it is treated as
+ * The callback function must return 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * - :macro:`NGTCP2_ERR_CRYPTO`
+ * - :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_PROTO`
+ * - :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
+ * - :macro:`NGTCP2_ERR_NOMEM`
+ * - :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ *
+ * If the other value is returned, it is treated as
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ *
+ * If application encounters fatal error, return
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
*/
typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn,
ngtcp2_crypto_level crypto_level,
@@ -2369,15 +2642,15 @@ typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn,
* @functypedef
*
* :type:`ngtcp2_recv_retry` is invoked when Retry packet is received.
- * This callback is client only.
+ * This callback is client use only.
*
* Application must regenerate packet protection key, IV, and header
* protection key for Initial packets using the destination connection
- * ID obtained by `ngtcp2_conn_get_dcid()` and install them by calling
- * `ngtcp2_conn_install_initial_key()`.
+ * ID obtained by :member:`hd->scid <ngtcp2_pkt_hd.scid>` and install
+ * them by calling `ngtcp2_conn_install_initial_key()`.
*
- * 0-RTT data accepted by the ngtcp2 library will be retransmitted by
- * the library automatically.
+ * 0-RTT data accepted by the ngtcp2 library will be automatically
+ * retransmitted as 0-RTT data by the library.
*
* The callback function must return 0 if it succeeds. Returning
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
@@ -2394,13 +2667,13 @@ typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
* encrypt is passed as |plaintext| of length |plaintextlen|. The
* AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context
* object which is initialized with encryption key. The nonce is
- * passed as |nonce| of length |noncelen|. The ad, Additional Data to
- * AEAD, is passed as |ad| of length |adlen|.
+ * passed as |nonce| of length |noncelen|. The Additional
+ * Authenticated Data is passed as |aad| of length |aadlen|.
*
* The implementation of this callback must encrypt |plaintext| using
* the negotiated cipher suite and write the ciphertext into the
* buffer pointed by |dest|. |dest| has enough capacity to store the
- * ciphertext.
+ * ciphertext and any additional AEAD tag data.
*
* |dest| and |plaintext| may point to the same buffer.
*
@@ -2412,7 +2685,7 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *plaintext, size_t plaintextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen);
+ const uint8_t *aad, size_t aadlen);
/**
* @functypedef
@@ -2422,8 +2695,8 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
* decrypt is passed as |ciphertext| of length |ciphertextlen|. The
* AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context
* object which is initialized with decryption key. The nonce is
- * passed as |nonce| of length |noncelen|. The ad, Additional Data to
- * AEAD, is passed as |ad| of length |adlen|.
+ * passed as |nonce| of length |noncelen|. The Additional
+ * Authenticated Data is passed as |aad| of length |aadlen|.
*
* The implementation of this callback must decrypt |ciphertext| using
* the negotiated cipher suite and write the ciphertext into the
@@ -2433,34 +2706,38 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
* |dest| and |ciphertext| may point to the same buffer.
*
* The callback function must return 0 if it succeeds. If TLS stack
- * fails to decrypt data, return :macro:`NGTCP2_ERR_TLS_DECRYPT`. For
- * any other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which
+ * fails to decrypt data, return :macro:`NGTCP2_ERR_DECRYPT`. For any
+ * other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which
* makes the library call return immediately.
*/
typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *ciphertext, size_t ciphertextlen,
const uint8_t *nonce, size_t noncelen,
- const uint8_t *ad, size_t adlen);
+ const uint8_t *aad, size_t aadlen);
/**
* @functypedef
*
* :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the
- * application to produce mask to encrypt or decrypt packet header.
+ * application to produce a mask to encrypt or decrypt packet header.
* The encryption cipher is |hp|. |hp_ctx| is the cipher context
* object which is initialized with header protection key. The sample
- * is passed as |sample|.
+ * is passed as |sample| which is :macro:`NGTCP2_HP_SAMPLELEN` bytes
+ * long.
*
* The implementation of this callback must produce a mask using the
* header protection cipher suite specified by QUIC specification and
* write the result into the buffer pointed by |dest|. The length of
- * mask must be :macro:`NGTCP2_HP_MASKLEN`. The library ensures that
- * |dest| has enough capacity.
+ * the mask must be at least :macro:`NGTCP2_HP_MASKLEN`. The library
+ * only uses the first :macro:`NGTCP2_HP_MASKLEN` bytes of the
+ * produced mask. The buffer pointed by |dest| is guaranteed to have
+ * at least :macro:`NGTCP2_HP_SAMPLELEN` bytes available for
+ * convenience.
*
* The callback function must return 0 if it succeeds, or
- * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
- * return immediately.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
*/
typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx,
@@ -2477,7 +2754,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
*
* :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00
+#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00u
/**
* @macro
@@ -2485,7 +2762,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
* :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` indicates that this chunk of
* data is final piece of an incoming stream.
*/
-#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01
+#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01u
/**
* @macro
@@ -2494,21 +2771,21 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
* data contains data received in 0RTT packet and the handshake has
* not completed yet, which means that the data might be replayed.
*/
-#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02
+#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02u
/**
* @functypedef
*
* :type:`ngtcp2_recv_stream_data` is invoked when stream data is
* received. The stream is specified by |stream_id|. |flags| is the
- * bitwise-OR of zero or more of NGTCP2_STREAM_DATA_FLAG_*. See
- * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE`. If |flags| &
+ * bitwise-OR of zero or more of :macro:`NGTCP2_STREAM_DATA_FLAG_*
+ * <NGTCP2_STREAM_DATA_FLAG_NONE>`. If |flags| &
* :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of
* the data is the last data in this stream. |offset| is the offset
* where this data begins. The library ensures that data is passed to
- * the application in the non-decreasing order of |offset|. The data
- * is passed as |data| of length |datalen|. |datalen| may be 0 if and
- * only if |fin| is nonzero.
+ * the application in the non-decreasing order of |offset| without any
+ * overlap. The data is passed as |data| of length |datalen|.
+ * |datalen| may be 0 if and only if |fin| is nonzero.
*
* If :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` is set in |flags|, it
* indicates that a part of or whole data was received in 0RTT packet
@@ -2539,20 +2816,54 @@ typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id,
void *user_data);
/**
+ * @macrosection
+ *
+ * Stream close flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_STREAM_CLOSE_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` indicates that
+ * app_error_code parameter is set.
+ */
+#define NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET 0x01u
+
+/**
* @functypedef
*
* :type:`ngtcp2_stream_close` is invoked when a stream is closed.
* This callback is not called when QUIC connection is closed before
- * existing streams are closed. |app_error_code| indicates the error
- * code of this closure.
+ * existing streams are closed. |flags| is the bitwise-OR of zero or
+ * more of :macro:`NGTCP2_STREAM_CLOSE_FLAG_*
+ * <NGTCP2_STREAM_CLOSE_FLAG_NONE>`. |app_error_code| indicates the
+ * error code of this closure if
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is set in
+ * |flags|. If it is not set, the stream was closed without any error
+ * code, which generally means success.
+ *
+ * |app_error_code| is the first application error code sent by a
+ * local endpoint, or received from a remote endpoint. If a stream is
+ * closed cleanly, no application error code is exchanged. Since QUIC
+ * stack does not know the application error code which indicates "no
+ * errors", |app_error_code| is set to 0 and
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is not set in
+ * |flags| in this case.
*
* The implementation of this callback should return 0 if it succeeds.
* Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
* call return immediately.
*/
-typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, int64_t stream_id,
- uint64_t app_error_code, void *user_data,
- void *stream_user_data);
+typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t app_error_code,
+ void *user_data, void *stream_user_data);
/**
* @functypedef
@@ -2575,10 +2886,10 @@ typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id,
* which is called when stream data is acked, and application can free
* the data. The acked range of data is [offset, offset + datalen).
* For a given stream_id, this callback is called sequentially in
- * increasing order of |offset|. |datalen| is normally strictly
- * greater than 0. One exception is that when a packet which includes
- * STREAM frame which has fin flag set, and 0 length data, this
- * callback is invoked with 0 passed as |datalen|.
+ * increasing order of |offset| without any overlap. |datalen| is
+ * normally strictly greater than 0. One exception is that when a
+ * packet which includes STREAM frame which has fin flag set, and 0
+ * length data, this callback is invoked with 0 passed as |datalen|.
*
* If a stream is closed prematurely and stream data is still
* in-flight, this callback function is not called for those data.
@@ -2594,26 +2905,6 @@ typedef int (*ngtcp2_acked_stream_data_offset)(
/**
* @functypedef
*
- * :type:`ngtcp2_acked_crypto_offset` is a callback function which is
- * called when crypto stream data is acknowledged, and application can
- * free the data. |crypto_level| indicates the encryption level where
- * this data was sent. Crypto data never be sent in
- * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`. This works
- * like :type:`ngtcp2_acked_stream_data_offset` but crypto stream has
- * no stream_id and stream_user_data, and |datalen| never become 0.
- *
- * The implementation of this callback should return 0 if it succeeds.
- * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
- * call return immediately.
- */
-typedef int (*ngtcp2_acked_crypto_offset)(ngtcp2_conn *conn,
- ngtcp2_crypto_level crypto_level,
- uint64_t offset, uint64_t datalen,
- void *user_data);
-
-/**
- * @functypedef
- *
* :type:`ngtcp2_recv_stateless_reset` is a callback function which is
* called when Stateless Reset packet is received. The stateless
* reset details are given in |sr|.
@@ -2663,16 +2954,11 @@ typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn,
*
* :type:`ngtcp2_rand` is a callback function to get randomized byte
* string from application. Application must fill random |destlen|
- * bytes to the buffer pointed by |dest|. |usage| provides the usage
- * of the generated random data.
- *
- * The callback function must return 0 if it succeeds. Returning
- * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
- * immediately.
+ * bytes to the buffer pointed by |dest|. The generated bytes are
+ * used only in non-cryptographic context.
*/
-typedef int (*ngtcp2_rand)(uint8_t *dest, size_t destlen,
- const ngtcp2_rand_ctx *rand_ctx,
- ngtcp2_rand_usage usage);
+typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx);
/**
* @functypedef
@@ -2741,11 +3027,35 @@ typedef int (*ngtcp2_update_key)(
size_t secretlen, void *user_data);
/**
+ * @macrosection
+ *
+ * Path validation related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_PATH_VALIDATION_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR` indicates the
+ * validation involving server preferred address. This flag is only
+ * set for client.
+ */
+#define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01u
+
+/**
* @functypedef
*
* :type:`ngtcp2_path_validation` is a callback function which tells
- * the application the outcome of path validation. |path| is the path
- * that was validated. If |res| is
+ * the application the outcome of path validation. |flags| is zero or
+ * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_*
+ * <NGTCP2_PATH_VALIDATION_FLAG_NONE>`. |path| is the path that was
+ * validated. If |res| is
* :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`,
* the path validation succeeded. If |res| is
* :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`,
@@ -2755,7 +3065,7 @@ typedef int (*ngtcp2_update_key)(
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
* immediately.
*/
-typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn,
+typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, uint32_t flags,
const ngtcp2_path *path,
ngtcp2_path_validation_result res,
void *user_data);
@@ -2766,16 +3076,28 @@ typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn,
* :type:`ngtcp2_select_preferred_addr` is a callback function which
* asks a client application to choose server address from preferred
* addresses |paddr| received from server. An application should
- * write preferred address in |dest|. If an application denies the
- * preferred addresses, just leave |dest| unmodified (or set dest->len
- * to 0) and return 0.
+ * write a network path for a selected preferred address in |dest|.
+ * More specifically, the selected preferred address must be set to
+ * :member:`dest->remote <ngtcp2_path.remote>`, a client source
+ * address must be set to :member:`dest->local <ngtcp2_path.local>`.
+ * If a client source address does not change for the new server
+ * address, leave :member:`dest->local <ngtcp2_path.local>`
+ * unmodified, or copy the value of :member:`local
+ * <ngtcp2_path.local>` field of the current network path obtained
+ * from `ngtcp2_conn_get_path()`. Both :member:`dest->local.addr
+ * <ngtcp2_addr.addr>` and :member:`dest->remote.addr
+ * <ngtcp2_addr.addr>` point to buffers which are at least
+ * ``sizeof(struct sockaddr_storage)`` bytes long, respectively. If
+ * an application denies the preferred addresses, just leave |dest|
+ * unmodified (or set :member:`dest->remote.addrlen
+ * <ngtcp2_addr.addrlen>` to 0) and return 0.
*
* The callback function must return 0 if it succeeds. Returning
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
* immediately.
*/
typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn,
- ngtcp2_addr *dest,
+ ngtcp2_path *dest,
const ngtcp2_preferred_addr *paddr,
void *user_data);
@@ -2840,7 +3162,9 @@ typedef int (*ngtcp2_recv_new_token)(ngtcp2_conn *conn, const ngtcp2_vec *token,
* @functypedef
*
* :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which
- * must delete the native object pointed by |aead_ctx|->native_handle.
+ * must delete the native object pointed by
+ * :member:`aead_ctx->native_handle
+ * <ngtcp2_crypto_aead_ctx.native_handle>`.
*/
typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn,
ngtcp2_crypto_aead_ctx *aead_ctx,
@@ -2851,7 +3175,8 @@ typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn,
*
* :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function
* which must delete the native object pointed by
- * |cipher_ctx|->native_handle.
+ * :member:`cipher_ctx->native_handle
+ * <ngtcp2_crypto_cipher_ctx.native_handle>`.
*/
typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data);
@@ -2867,7 +3192,7 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
*
* :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_DATAGRAM_FLAG_NONE 0x00
+#define NGTCP2_DATAGRAM_FLAG_NONE 0x00u
/**
* @macro
@@ -2876,14 +3201,14 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
* is received in 0RTT packet and the handshake has not completed yet,
* which means that the data might be replayed.
*/
-#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01
+#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01u
/**
* @functypedef
*
* :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is
* received. |flags| is bitwise-OR of zero or more of
- * NGTCP2_DATAGRAM_FLAG_*. See :macro:`NGTCP2_DATAGRAM_FLAG_NONE`.
+ * :macro:`NGTCP2_DATAGRAM_FLAG_* <NGTCP2_DATAGRAM_FLAG_NONE>`.
*
* If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it
* indicates that DATAGRAM frame was received in 0RTT packet and a
@@ -2898,6 +3223,107 @@ typedef int (*ngtcp2_recv_datagram)(ngtcp2_conn *conn, uint32_t flags,
void *user_data);
/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_ack_datagram` is invoked when a packet which contains
+ * DATAGRAM frame which is identified by |dgram_id| is acknowledged.
+ * |dgram_id| is the valued passed to `ngtcp2_conn_writev_datagram`.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_ack_datagram)(ngtcp2_conn *conn, uint64_t dgram_id,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_lost_datagram` is invoked when a packet which
+ * contains DATAGRAM frame which is identified by |dgram_id| is
+ * declared lost. |dgram_id| is the valued passed to
+ * `ngtcp2_conn_writev_datagram`. Note that the loss might be
+ * spurious, and DATAGRAM frame might be acknowledged later.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_lost_datagram)(ngtcp2_conn *conn, uint64_t dgram_id,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_get_path_challenge_data` is a callback function to
+ * ask an application for new data that is sent in PATH_CHALLENGE
+ * frame. Application must generate new unpredictable exactly
+ * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes of random data and
+ * store them into the buffer pointed by |data|.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_get_path_challenge_data)(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_stream_stop_sending` is invoked when a stream is no
+ * longer read by a local endpoint before it receives all stream data.
+ * This function is called at most once per stream. |app_error_code|
+ * is the error code passed to `ngtcp2_conn_shutdown_stream_read` or
+ * `ngtcp2_conn_shutdown_stream`.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_version_negotiation` is invoked when the compatible
+ * version negotiation takes place. For client, it is called when it
+ * sees a change in version field of a long header packet. This
+ * callback function might be called multiple times for client. For
+ * server, it is called once when the version is negotiated.
+ *
+ * The implementation of this callback must install new Initial keys
+ * for |version|. Use `ngtcp2_conn_install_vneg_initial_key` to
+ * install keys.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_key` is invoked when new key is installed to
+ * |conn| during QUIC cryptographic handshake.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_key)(ngtcp2_conn *conn, ngtcp2_crypto_level level,
+ void *user_data);
+
+#define NGTCP2_CALLBACKS_VERSION_V1 1
+#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_VERSION_V1
+
+/**
* @struct
*
* :type:`ngtcp2_callbacks` holds a set of callback functions.
@@ -2906,13 +3332,14 @@ typedef struct ngtcp2_callbacks {
/**
* :member:`client_initial` is a callback function which is invoked
* when client asks TLS stack to produce first TLS cryptographic
- * handshake message. This callback function must be specified.
+ * handshake message. This callback function must be specified for
+ * a client application.
*/
ngtcp2_client_initial client_initial;
/**
* :member:`recv_client_initial` is a callback function which is
* invoked when a server receives the first packet from client.
- * This callback function must be specified.
+ * This callback function must be specified for a server application.
*/
ngtcp2_recv_client_initial recv_client_initial;
/**
@@ -2957,14 +3384,6 @@ typedef struct ngtcp2_callbacks {
*/
ngtcp2_recv_stream_data recv_stream_data;
/**
- * :member:`acked_crypto_offset` is a callback function which is
- * invoked when CRYPTO data is acknowledged by a remote endpoint.
- * It tells an application the largest offset of acknowledged CRYPTO
- * data without a gap so that the application can free memory for
- * the data. This callback function is optional.
- */
- ngtcp2_acked_crypto_offset acked_crypto_offset;
- /**
* :member:`acked_stream_data_offset` is a callback function which
* is invoked when STREAM data, which includes application data, is
* acknowledged by a remote endpoint. It tells an application the
@@ -3012,8 +3431,8 @@ typedef struct ngtcp2_callbacks {
ngtcp2_extend_max_streams extend_max_local_streams_uni;
/**
* :member:`rand` is a callback function which is invoked when the
- * library needs unpredictable sequence of random data. This
- * callback function must be specified.
+ * library needs sequence of random data. This callback function
+ * must be specified.
*/
ngtcp2_rand rand;
/**
@@ -3031,18 +3450,21 @@ typedef struct ngtcp2_callbacks {
/**
* :member:`update_key` is a callback function which is invoked when
* the library tells an application that it must update keying
- * materials and install new keys. This function must be specified.
+ * materials and install new keys. This callback function must be
+ * specified.
*/
ngtcp2_update_key update_key;
/**
* :member:`path_validation` is a callback function which is invoked
- * when path validation completed. This function is optional.
+ * when path validation completed. This callback function is
+ * optional.
*/
ngtcp2_path_validation path_validation;
/**
* :member:`select_preferred_addr` is a callback function which is
* invoked when the library asks a client to select preferred
- * address presented by a server. This function is optional.
+ * address presented by a server. This callback function is
+ * optional.
*/
ngtcp2_select_preferred_addr select_preferred_addr;
/**
@@ -3075,47 +3497,97 @@ typedef struct ngtcp2_callbacks {
/**
* :member:`dcid_status` is a callback function which is invoked
* when the new destination Connection ID is activated or the
- * activated destination Connection ID is now deactivated.
+ * activated destination Connection ID is now deactivated. This
+ * callback function is optional.
*/
ngtcp2_connection_id_status dcid_status;
/**
* :member:`handshake_confirmed` is a callback function which is
* invoked when both endpoints agree that handshake has finished.
* This field is ignored by server because handshake_completed
- * indicates the handshake confirmation for server.
+ * indicates the handshake confirmation for server. This callback
+ * function is optional.
*/
ngtcp2_handshake_confirmed handshake_confirmed;
/**
* :member:`recv_new_token` is a callback function which is invoked
* when new token is received from server. This field is ignored by
- * server.
+ * server. This callback function is optional.
*/
ngtcp2_recv_new_token recv_new_token;
/**
* :member:`delete_crypto_aead_ctx` is a callback function which
- * deletes a given AEAD cipher context object.
+ * deletes a given AEAD cipher context object. This callback
+ * function must be specified.
*/
ngtcp2_delete_crypto_aead_ctx delete_crypto_aead_ctx;
/**
* :member:`delete_crypto_cipher_ctx` is a callback function which
- * deletes a given cipher context object.
+ * deletes a given cipher context object. This callback function
+ * must be specified.
*/
ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx;
/**
* :member:`recv_datagram` is a callback function which is invoked
- * when DATAGRAM frame is received.
+ * when DATAGRAM frame is received. This callback function is
+ * optional.
*/
ngtcp2_recv_datagram recv_datagram;
+ /**
+ * :member:`ack_datagram` is a callback function which is invoked
+ * when a packet containing DATAGRAM frame is acknowledged. This
+ * callback function is optional.
+ */
+ ngtcp2_ack_datagram ack_datagram;
+ /**
+ * :member:`lost_datagram` is a callback function which is invoked
+ * when a packet containing DATAGRAM frame is declared lost. This
+ * callback function is optional.
+ */
+ ngtcp2_lost_datagram lost_datagram;
+ /**
+ * :member:`get_path_challenge_data` is a callback function which is
+ * invoked when the library needs new PATH_CHALLENGE data. This
+ * callback must be specified.
+ */
+ ngtcp2_get_path_challenge_data get_path_challenge_data;
+ /**
+ * :member:`stream_stop_sending` is a callback function which is
+ * invoked when a local endpoint no longer reads from a stream
+ * before it receives all stream data. This callback function is
+ * optional.
+ */
+ ngtcp2_stream_stop_sending stream_stop_sending;
+ /**
+ * :member:`version_negotiation` is a callback function which is
+ * invoked when the compatible version negotiation takes place.
+ * This callback function must be specified.
+ */
+ ngtcp2_version_negotiation version_negotiation;
+ /**
+ * :member:`recv_rx_key` is a callback function which is invoked
+ * when a new key for decrypting packets is installed during QUIC
+ * cryptographic handshake. It is not called for
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`.
+ */
+ ngtcp2_recv_key recv_rx_key;
+ /**
+ * :member:`recv_tx_key` is a callback function which is invoked
+ * when a new key for encrypting packets is installed during QUIC
+ * cryptographic handshake. It is not called for
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`.
+ */
+ ngtcp2_recv_key recv_tx_key;
} ngtcp2_callbacks;
/**
* @function
*
* `ngtcp2_pkt_write_connection_close` writes Initial packet
- * containing CONNECTION_CLOSE frame with the given |error_code| to
- * the buffer pointed by |dest| of length |destlen|. All encryption
- * parameters are for Initial packet encryption. The packet number is
- * always 0.
+ * containing CONNECTION_CLOSE frame with the given |error_code| and
+ * the optional |reason| of length |reasonlen| to the buffer pointed
+ * by |dest| of length |destlen|. All encryption parameters are for
+ * Initial packet encryption. The packet number is always 0.
*
* The primary use case of this function is for server to send
* CONNECTION_CLOSE frame in Initial packet to close connection
@@ -3131,20 +3603,24 @@ typedef struct ngtcp2_callbacks {
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close(
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt,
- const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx,
- const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv,
+ ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx);
/**
* @function
*
* `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed
- * by |dest| whose length is |destlen|. |odcid| specifies Original
- * Destination Connection ID. |token| specifies Retry Token, and
- * |tokenlen| specifies its length. |aead| must be AEAD_AES_128_GCM.
- * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as an
- * encryption key.
+ * by |dest| whose length is |destlen|. |dcid| is the destination
+ * connection ID which appeared in a packet as a source connection ID
+ * sent by client. |scid| is a server chosen source connection ID.
+ * |odcid| specifies Original Destination Connection ID which appeared
+ * in a packet as a destination connection ID sent by client. |token|
+ * specifies Retry Token, and |tokenlen| specifies its length. |aead|
+ * must be AEAD_AES_128_GCM. |aead_ctx| must be initialized with
+ * :macro:`NGTCP2_RETRY_KEY` as an encryption key.
*
* This function returns the number of bytes written to the buffer, or
* one of the following negative error codes:
@@ -3153,6 +3629,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close(
* Buffer is too small.
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
* Callback function failed.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * :member:`odcid->datalen <ngtcp2_cid.datalen>` is less than
+ * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN`.
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
@@ -3164,15 +3643,21 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(
* @function
*
* `ngtcp2_accept` is used by server implementation, and decides
- * whether packet |pkt| of length |pktlen| is acceptable for initial
- * packet from client.
+ * whether packet |pkt| of length |pktlen| from client is acceptable
+ * for the very initial packet to a connection.
+ *
+ * If |dest| is not ``NULL`` and the function returns 0, or
+ * :macro:`NGTCP2_ERR_RETRY`, the decoded packet header is stored to
+ * the object pointed by |dest|.
*
- * If it is acceptable, it returns 0. If it is not acceptable, and
- * Version Negotiation packet is required to send, it returns 1.
- * Otherwise, it returns -1.
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
*
- * If |dest| is not ``NULL``, and the return value is 0 or 1, the
- * decoded packet header is stored to the object pointed by |dest|.
+ * :macro:`NGTCP2_ERR_RETRY`
+ * Retry packet should be sent.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The packet is not acceptable for the very first packet to a new
+ * connection; or the function failed to parse the packet header.
*/
NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
size_t pktlen);
@@ -3182,15 +3667,16 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
*
* `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and
* initializes it as client. |dcid| is randomized destination
- * connection ID. |scid| is source connection ID. |version| is a
- * QUIC version to use. |path| is the network path where this QUIC
- * connection is being established and must not be ``NULL``.
- * |callbacks|, |settings|, and |params| must not be ``NULL``, and the
- * function make a copy of each of them. |params| is local QUIC
- * transport parameters and sent to a remote endpoint during
- * handshake. |user_data| is the arbitrary pointer which is passed to
- * the user-defined callback functions. If |mem| is ``NULL``, the
- * memory allocator returned by `ngtcp2_mem_default()` is used.
+ * connection ID. |scid| is source connection ID.
+ * |client_chosen_version| is a QUIC version that a client chooses.
+ * |path| is the network path where this QUIC connection is being
+ * established and must not be ``NULL``. |callbacks|, |settings|, and
+ * |params| must not be ``NULL``, and the function make a copy of each
+ * of them. |params| is local QUIC transport parameters and sent to a
+ * remote endpoint during handshake. |user_data| is the arbitrary
+ * pointer which is passed to the user-defined callback functions. If
+ * |mem| is ``NULL``, the memory allocator returned by
+ * `ngtcp2_mem_default()` is used.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -3198,13 +3684,13 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory.
*/
-NGTCP2_EXTERN int
-ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid, const ngtcp2_path *path,
- uint32_t version, const ngtcp2_callbacks *callbacks,
- const ngtcp2_settings *settings,
- const ngtcp2_transport_params *params,
- const ngtcp2_mem *mem, void *user_data);
+NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data);
/**
* @function
@@ -3212,14 +3698,14 @@ ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
* `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and
* initializes it as server. |dcid| is a destination connection ID.
* |scid| is a source connection ID. |path| is the network path where
- * this QUIC connection is being established and must not be ``NULL`.
- * |version| is a QUIC version to use. |callbacks|, |settings|, and
- * |params| must not be ``NULL``, and the function make a copy of each
- * of them. |params| is local QUIC transport parameters and sent to a
- * remote endpoint during handshake. |user_data| is the arbitrary
- * pointer which is passed to the user-defined callback functions. If
- * |mem| is ``NULL``, the memory allocator returned by
- * `ngtcp2_mem_default()` is used.
+ * this QUIC connection is being established and must not be ``NULL``.
+ * |client_chosen_version| is a QUIC version that a client chooses.
+ * |callbacks|, |settings|, and |params| must not be ``NULL``, and the
+ * function make a copy of each of them. |params| is local QUIC
+ * transport parameters and sent to a remote endpoint during
+ * handshake. |user_data| is the arbitrary pointer which is passed to
+ * the user-defined callback functions. If |mem| is ``NULL``, the
+ * memory allocator returned by `ngtcp2_mem_default()` is used.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -3227,13 +3713,13 @@ ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory.
*/
-NGTCP2_EXTERN int
-ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid, const ngtcp2_path *path,
- uint32_t version, const ngtcp2_callbacks *callbacks,
- const ngtcp2_settings *settings,
- const ngtcp2_transport_params *params,
- const ngtcp2_mem *mem, void *user_data);
+NGTCP2_EXTERN int ngtcp2_conn_server_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data);
/**
* @function
@@ -3249,56 +3735,59 @@ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn);
* `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of
* length |pktlen| and processes it. |path| is the network path the
* packet is delivered and must not be ``NULL``. |pi| is packet
- * metadata and must not be ``NULL``. This function performs QUIC
- * handshake as well.
+ * metadata and may be ``NULL``. This function performs QUIC handshake
+ * as well.
*
* This function must not be called from inside the callback
* functions.
*
* This function returns 0 if it succeeds, or negative error codes.
- * In general, if the error code which satisfies
- * `ngtcp2_err_is_fatal(err) <ngtcp2_err_is_fatal>` != 0 is returned,
- * the application should just close the connection by calling
- * `ngtcp2_conn_write_connection_close` or just delete the QUIC
- * connection using `ngtcp2_conn_del`. It is undefined to call the
- * other library functions. If :macro:`NGTCP2_ERR_RETRY` is returned,
- * application must be a server and it must perform address validation
- * by sending Retry packet and close the connection. If
+ * If :macro:`NGTCP2_ERR_RETRY` is returned, application must be a
+ * server and it must perform address validation by sending Retry
+ * packet and discard the connection state. If
* :macro:`NGTCP2_ERR_DROP_CONN` is returned, server application must
* drop the connection silently (without sending any CONNECTION_CLOSE
- * frame) and discard connection state.
+ * frame) and discard connection state. If
+ * :macro:`NGTCP2_ERR_DRAINING` is returned, a connection has entered
+ * the draining state, and no further packet transmission is allowed.
+ * If :macro:`NGTCP2_ERR_CRYPTO` is returned, the error happened in
+ * TLS stack and `ngtcp2_conn_get_tls_alert` returns TLS alert if set.
+ *
+ * If any other negative errors are returned, call
+ * `ngtcp2_conn_write_connection_close` to get terminal packet, and
+ * sending it makes QUIC connection enter the closing state.
*/
-NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn,
- const ngtcp2_path *path,
- const ngtcp2_pkt_info *pi,
- const uint8_t *pkt, size_t pktlen,
- ngtcp2_tstamp ts);
+NGTCP2_EXTERN int
+ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
+ int pkt_info_version, const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts);
/**
* @function
*
* `ngtcp2_conn_write_pkt` is equivalent to calling
- * `ngtcp2_conn_writev_stream` without specifying stream data and
+ * `ngtcp2_conn_writev_stream` with -1 as stream_id, no stream data, and
* :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags.
*/
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn,
- ngtcp2_path *path,
- ngtcp2_pkt_info *pi,
- uint8_t *dest, size_t destlen,
- ngtcp2_tstamp ts);
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts);
/**
* @function
*
- * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC
- * handshake has completed.
+ * `ngtcp2_conn_handshake_completed` tells |conn| that the TLS stack
+ * declares TLS handshake completion. This does not mean QUIC
+ * handshake has completed. The library needs extra conditions to be
+ * met.
*/
NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn);
/**
* @function
*
- * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake
+ * `ngtcp2_conn_get_handshake_completed` returns nonzero if QUIC handshake
* has completed.
*/
NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn);
@@ -3308,14 +3797,17 @@ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn);
*
* `ngtcp2_conn_install_initial_key` installs packet protection keying
* materials for Initial packets. |rx_aead_ctx| is AEAD cipher
- * context object and must be initialized with decryption key, IV
- * |rx_iv| of length |rx_ivlen|, and packet header protection cipher
- * context object |rx_hp_ctx| to decrypt incoming Initial packets.
+ * context object and must be initialized with a decryption key.
+ * |rx_iv| is IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is
+ * a packet header protection cipher context object for decryption.
* Similarly, |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for
* encrypting outgoing packets and are the same length with the
* decryption counterpart . If they have already been set, they are
* overwritten.
*
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
* If this function succeeds, |conn| takes ownership of |rx_aead_ctx|,
* |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|.
* :type:`ngtcp2_delete_crypto_aead_ctx` and
@@ -3342,12 +3834,50 @@ NGTCP2_EXTERN int ngtcp2_conn_install_initial_key(
/**
* @function
*
+ * `ngtcp2_conn_install_vneg_initial_key` installs packet protection
+ * keying materials for Initial packets on compatible version
+ * negotiation for |version|. |rx_aead_ctx| is AEAD cipher context
+ * object and must be initialized with a decryption key. |rx_iv| is
+ * IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is a packet
+ * header protection cipher context object for decryption. Similarly,
+ * |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for encrypting outgoing
+ * packets and are the same length with the decryption counterpart .
+ * If they have already been set, they are overwritten.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|,
+ * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|.
+ * :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv,
+ const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+ const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+ const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen);
+
+/**
+ * @function
+ *
* `ngtcp2_conn_install_rx_handshake_key` installs packet protection
* keying materials for decrypting incoming Handshake packets.
* |aead_ctx| is AEAD cipher context object which must be initialized
- * with decryption key, IV |iv| of length |ivlen|, and packet header
- * protection cipher context object |hp_ctx| to decrypt incoming
- * Handshake packets.
+ * with a decryption key. |iv| is IV of length |ivlen|. |hp_ctx| is
+ * a packet header protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
*
* If this function succeeds, |conn| takes ownership of |aead_ctx|,
* and |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
@@ -3371,9 +3901,11 @@ NGTCP2_EXTERN int ngtcp2_conn_install_rx_handshake_key(
* `ngtcp2_conn_install_tx_handshake_key` installs packet protection
* keying materials for encrypting outgoing Handshake packets.
* |aead_ctx| is AEAD cipher context object which must be initialized
- * with encryption key, IV |iv| of length |ivlen|, and packet header
- * protection cipher context object |hp_ctx| to encrypt outgoing
- * Handshake packets.
+ * with an encryption key. |iv| is IV of length |ivlen|. |hp_ctx| is
+ * a packet header protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
*
* If this function succeeds, |conn| takes ownership of |aead_ctx| and
* |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
@@ -3399,6 +3931,9 @@ NGTCP2_EXTERN int ngtcp2_conn_install_tx_handshake_key(
* packet header protection cipher context object |hp_ctx| to encrypt
* (for client) or decrypt (for server) 0RTT packets.
*
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
* If this function succeeds, |conn| takes ownership of |aead_ctx| and
* |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
* :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
@@ -3419,12 +3954,15 @@ NGTCP2_EXTERN int ngtcp2_conn_install_early_key(
* @function
*
* `ngtcp2_conn_install_rx_key` installs packet protection keying
- * materials for decrypting Short packets. |secret| of length
+ * materials for decrypting Short header packets. |secret| of length
* |secretlen| is the decryption secret which is used to derive keying
* materials passed to this function. |aead_ctx| is AEAD cipher
- * context object which must be initialized with decryption key, IV
- * |iv| of length |ivlen|, and packet header protection cipher context
- * object |hp_ctx| to decrypt incoming Short packets.
+ * context object which must be initialized with a decryption key.
+ * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header
+ * protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
*
* If this function succeeds, |conn| takes ownership of |aead_ctx| and
* |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
@@ -3447,12 +3985,15 @@ NGTCP2_EXTERN int ngtcp2_conn_install_rx_key(
* @function
*
* `ngtcp2_conn_install_tx_key` installs packet protection keying
- * materials for encrypting Short packets. |secret| of length
+ * materials for encrypting Short header packets. |secret| of length
* |secretlen| is the encryption secret which is used to derive keying
* materials passed to this function. |aead_ctx| is AEAD cipher
- * context object which must be initialized with encryption key, IV
- * |iv| of length |ivlen|, and packet header protection cipher context
- * object |hp_ctx| to encrypt outgoing Short packets.
+ * context object which must be initialized with an encryption key.
+ * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header
+ * protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
*
* If this function succeeds, |conn| takes ownership of |aead_ctx| and
* |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
@@ -3492,7 +4033,7 @@ NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn,
* `ngtcp2_conn_set_tls_error` sets the TLS related error |liberr| in
* |conn|. |liberr| must be one of ngtcp2 library error codes (which
* is defined as NGTCP2_ERR_* macro, such as
- * :macro:`NGTCP2_ERR_TLS_DECRYPT`). In general, error code should be
+ * :macro:`NGTCP2_ERR_DECRYPT`). In general, error code should be
* propagated via return value, but sometimes ngtcp2 API is called
* inside callback function of TLS stack and it does not allow to
* return ngtcp2 error code directly. In this case, implementation
@@ -3513,6 +4054,34 @@ NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn);
/**
* @function
*
+ * `ngtcp2_conn_set_tls_alert` sets a TLS alert |alert| generated by a
+ * local endpoint to |conn|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_tls_alert` returns the value set by
+ * `ngtcp2_conn_set_tls_alert`. If no value is set, this function
+ * returns 0.
+ */
+NGTCP2_EXTERN uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_keep_alive_timeout` sets keep-alive timeout. If
+ * nonzero value is given, after a connection is idle at least in a
+ * given amount of time, a keep-alive packet is sent. If 0 is set,
+ * keep-alive functionality is disabled and this is the default.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
+ ngtcp2_duration timeout);
+
+/**
+ * @function
+ *
* `ngtcp2_conn_get_expiry` returns the next expiry time.
*
* Call `ngtcp2_conn_handle_expiry()` and `ngtcp2_conn_write_pkt` (or
@@ -3532,15 +4101,6 @@ NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn,
/**
* @function
*
- * `ngtcp2_conn_get_idle_expiry` returns the time when a connection
- * should be closed if it continues to be idle. If idle timeout is
- * disabled, this function returns ``UINT64_MAX``.
- */
-NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn);
-
-/**
- * @function
- *
* `ngtcp2_conn_get_pto` returns Probe Timeout (PTO).
*/
NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn);
@@ -3548,51 +4108,71 @@ NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn);
/**
* @function
*
- * `ngtcp2_conn_set_remote_transport_params` sets transport parameter
- * |params| to |conn|.
+ * `ngtcp2_conn_decode_remote_transport_params` decodes QUIC transport
+ * parameters from the buffer pointed by |data| of length |datalen|,
+ * and sets the result to |conn|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
- * :macro:`NGTCP2_ERR_PROTO`
- * If |conn| is server, and negotiated_version field is not the
- * same as the used version.
+ * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * The required parameter is missing.
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * The input is malformed.
+ * :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
+ * Failed to validate the remote QUIC transport parameters.
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
+ * Version negotiation failure.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
*/
NGTCP2_EXTERN int
-ngtcp2_conn_set_remote_transport_params(ngtcp2_conn *conn,
- const ngtcp2_transport_params *params);
+ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn,
+ const uint8_t *data, size_t datalen);
/**
* @function
*
- * `ngtcp2_conn_get_remote_transport_params` fills settings values in
- * |params|. original_connection_id and
- * original_connection_id_present are always zero filled.
+ * `ngtcp2_conn_get_remote_transport_params` returns a pointer to the
+ * remote QUIC transport parameters. If no remote transport
+ * parameters are set, it returns NULL.
*/
-NGTCP2_EXTERN void
-ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn,
- ngtcp2_transport_params *params);
+NGTCP2_EXTERN const ngtcp2_transport_params *
+ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn);
/**
* @function
*
* `ngtcp2_conn_set_early_remote_transport_params` sets |params| as
- * transport parameter previously received from a server. The
- * parameters are used to send 0-RTT data. QUIC requires that client
- * application should remember transport parameter as well as session
- * ticket.
- *
- * At least following fields must be set:
- *
- * * initial_max_stream_id_bidi
- * * initial_max_stream_id_uni
- * * initial_max_stream_data_bidi_local
- * * initial_max_stream_data_bidi_remote
- * * initial_max_stream_data_uni
- * * initial_max_data
- */
-NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params(
- ngtcp2_conn *conn, const ngtcp2_transport_params *params);
+ * transport parameters previously received from a server. The
+ * parameters are used to send early data. QUIC requires that client
+ * application should remember transport parameters along with a
+ * session ticket.
+ *
+ * At least following fields should be set:
+ *
+ * - initial_max_stream_id_bidi
+ * - initial_max_stream_id_uni
+ * - initial_max_stream_data_bidi_local
+ * - initial_max_stream_data_bidi_remote
+ * - initial_max_stream_data_uni
+ * - initial_max_data
+ * - active_connection_id_limit
+ * - max_datagram_frame_size (if DATAGRAM extension was negotiated)
+ *
+ * The following fields are ignored:
+ *
+ * - ack_delay_exponent
+ * - max_ack_delay
+ * - initial_scid
+ * - original_dcid
+ * - preferred_address and preferred_address_present
+ * - retry_scid and retry_scid_present
+ * - stateless_reset_token and stateless_reset_token_present
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params);
/**
* @function
@@ -3611,19 +4191,35 @@ NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params(
* :macro:`NGTCP2_ERR_INVALID_STATE`
* `ngtcp2_conn_install_tx_handshake_key` has been called.
*/
-NGTCP2_EXTERN int
-ngtcp2_conn_set_local_transport_params(ngtcp2_conn *conn,
- const ngtcp2_transport_params *params);
+NGTCP2_EXTERN int ngtcp2_conn_set_local_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params);
/**
* @function
*
- * `ngtcp2_conn_get_local_transport_params` fills settings values in
- * |params|.
+ * `ngtcp2_conn_get_local_transport_params` returns a pointer to the
+ * local QUIC transport parameters.
*/
-NGTCP2_EXTERN void
-ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn,
- ngtcp2_transport_params *params);
+NGTCP2_EXTERN const ngtcp2_transport_params *
+ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_encode_local_transport_params` encodes the local QUIC
+ * transport parameters in |dest| of length |destlen|. This is
+ * equivalent to calling `ngtcp2_conn_get_local_transport_params` and
+ * then `ngtcp2_encode_transport_params`.
+ *
+ * This function returns the number of written, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_encode_local_transport_params(
+ ngtcp2_conn *conn, uint8_t *dest, size_t destlen);
/**
* @function
@@ -3636,7 +4232,7 @@ ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn,
* 0RTT packet, application can call this function after calling
* `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet,
* application can call this function after calling
- * `ngtcp2_conn_set_remote_transport_params` and
+ * `ngtcp2_conn_decode_remote_transport_params` and
* `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is
* used, application can call this function after calling
* `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet.
@@ -3664,7 +4260,7 @@ NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn,
* 0RTT packet, application can call this function after calling
* `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet,
* application can call this function after calling
- * `ngtcp2_conn_set_remote_transport_params` and
+ * `ngtcp2_conn_decode_remote_transport_params` and
* `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is
* used, application can call this function after calling
* `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet.
@@ -3698,8 +4294,6 @@ NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn,
*
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- * Stream does not exist
*/
NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn,
int64_t stream_id,
@@ -3720,8 +4314,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn,
*
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- * Stream does not exist
*/
NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn,
int64_t stream_id,
@@ -3741,8 +4333,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn,
*
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- * Stream does not exist
*/
NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
int64_t stream_id,
@@ -3759,7 +4349,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
*
* :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00
+#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00u
/**
* @macro
@@ -3767,7 +4357,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
* :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` indicates that more data may
* come and should be coalesced into the same packet if possible.
*/
-#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01
+#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01u
/**
* @macro
@@ -3775,7 +4365,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
* :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` indicates that the passed
* data is the final part of a stream.
*/
-#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02
+#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u
/**
* @function
@@ -3784,10 +4374,11 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
* `ngtcp2_conn_writev_stream`. The only difference is that it
* conveniently accepts a single buffer.
*/
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
- ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id,
- const uint8_t *data, size_t datalen, ngtcp2_tstamp ts);
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen,
+ ngtcp2_tstamp ts);
/**
* @function
@@ -3797,6 +4388,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
* pointed by |dest| of length |destlen|. This function performs QUIC
* handshake as well.
*
+ * |destlen| should be at least
+ * :member:`ngtcp2_settings.max_udp_payload_size`.
+ *
* Specifying -1 to |stream_id| means no new stream data to send.
*
* If |path| is not ``NULL``, this function stores the network path
@@ -3839,8 +4433,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
* by default, application can only send 1 STREAM frame in one QUIC
* packet. In order to include more than 1 STREAM frame in one QUIC
* packet, specify :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|.
- * This is analogous to ``MSG_MORE`` flag in ``send(2)``. If the
- * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4
+ * This is analogous to ``MSG_MORE`` flag in :manpage:`send(2)`. If
+ * the :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4
* outcomes:
*
* - The function returns the written length of packet just like
@@ -3865,12 +4459,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
*
* When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not
* call other ngtcp2 API functions (application can still call
- * `ngtcp2_conn_write_connection_close` or
- * `ngtcp2_conn_write_application_close` to handle error from this
+ * `ngtcp2_conn_write_connection_close` to handle error from this
* function). Just keep calling `ngtcp2_conn_writev_stream`,
* `ngtcp2_conn_write_pkt`, or `ngtcp2_conn_writev_datagram` until it
* returns a positive number (which indicates a complete packet is
- * ready).
+ * ready). If there is no stream data to include, call this function
+ * with |stream_id| as -1 to stop coalescing and write a packet.
*
* This function returns 0 if it cannot write any frame because buffer
* is too small, or packet is congestion limited. Application should
@@ -3879,6 +4473,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
* This function must not be called from inside the callback
* functions.
*
+ * If pacing is enabled, `ngtcp2_conn_update_pkt_tx_time` must be
+ * called after this function. Application may call this function
+ * multiple times before calling `ngtcp2_conn_update_pkt_tx_time`.
+ * Packet pacing is enabled if BBR congestion controller algorithm is
+ * used.
+ *
* This function returns the number of bytes written in |dest| if it
* succeeds, or one of the following negative error codes:
*
@@ -3892,6 +4492,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
* Packet number is exhausted, and cannot send any more packet.
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
* User callback failed
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The total length of stream data is too large.
* :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`
* Stream is blocked because of flow control.
* :macro:`NGTCP2_ERR_WRITE_MORE`
@@ -3906,10 +4508,11 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
* connection using `ngtcp2_conn_del`. It is undefined to call the
* other library functions.
*/
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
- ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id,
- const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts);
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts);
/**
* @macrosection
@@ -3922,7 +4525,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
*
* :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00
+#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00u
/**
* @macro
@@ -3930,7 +4533,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
* :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` indicates that more data
* may come and should be coalesced into the same packet if possible.
*/
-#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01
+#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u
/**
* @function
@@ -3940,6 +4543,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
* |dest| of length |destlen|. This function performs QUIC handshake
* as well.
*
+ * |destlen| should be at least
+ * :member:`ngtcp2_settings.max_udp_payload_size`.
+ *
* For |path| and |pi| parameters, refer to
* `ngtcp2_conn_writev_stream`.
*
@@ -3947,6 +4553,14 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
* assigned to |*paccepted| if it is not NULL. The data in DATAGRAM
* frame cannot be fragmented; writing partial data is not possible.
*
+ * |dgram_id| is an opaque identifier which should uniquely identify
+ * the given DATAGRAM. It is passed to :type:`ngtcp2_ack_datagram`
+ * callback when a packet that contains DATAGRAM frame is
+ * acknowledged. It is passed to :type:`ngtcp2_lost_datagram`
+ * callback when a packet that contains DATAGRAM frame is declared
+ * lost. If an application uses neither of those callbacks, it can
+ * sets 0 to this parameter.
+ *
* This function might write other frames other than DATAGRAM, just
* like `ngtcp2_conn_writev_stream`.
*
@@ -3978,8 +4592,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
*
* When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not
* call other ngtcp2 API functions (application can still call
- * `ngtcp2_conn_write_connection_close` or
- * `ngtcp2_conn_write_application_close` to handle error from this
+ * `ngtcp2_conn_write_connection_close` to handle error from this
* function). Just keep calling `ngtcp2_conn_writev_datagram`,
* `ngtcp2_conn_writev_stream` or `ngtcp2_conn_write_pkt` until it
* returns a positive number (which indicates a complete packet is
@@ -4011,96 +4624,17 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
* connection using `ngtcp2_conn_del`. It is undefined to call the
* other library functions.
*/
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram(
- ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, int *paccepted, uint32_t flags, const ngtcp2_vec *datav,
- size_t datavcnt, ngtcp2_tstamp ts);
-
-/**
- * @function
- *
- * `ngtcp2_conn_write_connection_close` writes a packet which contains
- * a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed by
- * |dest| whose capacity is |datalen|.
- *
- * If |path| is not ``NULL``, this function stores the network path
- * with which the packet should be sent. Each addr field must point
- * to the buffer which should be at least ``sizeof(struct
- * sockaddr_storage)`` bytes long. The assignment might not be done
- * if nothing is written to |dest|.
- *
- * If |pi| is not ``NULL``, this function stores packet metadata in it
- * if it succeeds. The metadata includes ECN markings.
- *
- * This function must not be called from inside the callback
- * functions.
- *
- * At the moment, successful call to this function makes connection
- * close. We may change this behaviour in the future to allow
- * graceful shutdown.
- *
- * :macro:`NGTCP2_ERR_NOMEM`
- * Out of memory
- * :macro:`NGTCP2_ERR_NOBUF`
- * Buffer is too small
- * :macro:`NGTCP2_ERR_INVALID_STATE`
- * The current state does not allow sending CONNECTION_CLOSE.
- * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
- * Packet number is exhausted, and cannot send any more packet.
- * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
- * User callback failed
- */
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close(
- ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, uint64_t error_code, ngtcp2_tstamp ts);
-
-/**
- * @function
- *
- * `ngtcp2_conn_write_application_close` writes a packet which
- * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed
- * by |dest| whose capacity is |datalen|.
- *
- * If |path| is not ``NULL``, this function stores the network path
- * with which the packet should be sent. Each addr field must point
- * to the buffer which should be at least ``sizeof(struct
- * sockaddr_storage)`` bytes long. The assignment might not be done
- * if nothing is written to |dest|.
- *
- * If |pi| is not ``NULL``, this function stores packet metadata in it
- * if it succeeds. The metadata includes ECN markings.
- *
- * If handshake has not been confirmed yet, CONNECTION_CLOSE (type
- * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written
- * instead.
- *
- * This function must not be called from inside the callback
- * functions.
- *
- * At the moment, successful call to this function makes connection
- * close. We may change this behaviour in the future to allow
- * graceful shutdown.
- *
- * :macro:`NGTCP2_ERR_NOMEM`
- * Out of memory
- * :macro:`NGTCP2_ERR_NOBUF`
- * Buffer is too small
- * :macro:`NGTCP2_ERR_INVALID_STATE`
- * The current state does not allow sending CONNECTION_CLOSE.
- * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
- * Packet number is exhausted, and cannot send any more packet.
- * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
- * User callback failed
- */
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close(
- ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts);
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted,
+ uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts);
/**
* @function
*
* `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in
- * closing period.
+ * the closing period.
*/
NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn);
@@ -4108,7 +4642,7 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn);
* @function
*
* `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in
- * draining period.
+ * the draining period.
*/
NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn);
@@ -4121,8 +4655,8 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn);
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- * Stream was not found
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
*/
NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn,
int64_t stream_id,
@@ -4178,6 +4712,16 @@ NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn);
/**
* @function
*
+ * `ngtcp2_conn_get_client_initial_dcid` returns the non-NULL pointer
+ * to the Destination Connection ID that client sent in its Initial
+ * packet.
+ */
+NGTCP2_EXTERN const ngtcp2_cid *
+ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
* `ngtcp2_conn_get_num_scid` returns the number of source connection
* IDs which the local endpoint has provided to the peer and have not
* retired.
@@ -4249,49 +4793,47 @@ NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn,
/**
* @function
*
- * `ngtcp2_conn_get_negotiated_version` returns the negotiated version.
+ * `ngtcp2_conn_get_client_chosen_version` returns the client chosen
+ * version.
*/
-NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn);
+NGTCP2_EXTERN uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn);
/**
* @function
*
- * `ngtcp2_conn_early_data_rejected` tells |conn| that 0-RTT data was
- * rejected by a server.
+ * `ngtcp2_conn_get_negotiated_version` returns the negotiated version.
+ *
+ * Until the version is negotiated, this function returns 0.
*/
-NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn);
+NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn);
/**
* @function
*
- * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to
- * |*cstat|.
+ * `ngtcp2_conn_early_data_rejected` tells |conn| that early data was
+ * rejected by a server. |conn| discards the following connection
+ * states:
+ *
+ * - Any opended streams.
+ * - Stream identifier allocations.
+ * - Max data extended by `ngtcp2_conn_extend_max_offset`.
+ * - Max bidi streams extended by `ngtcp2_conn_extend_max_streams_bidi`.
+ * - Max uni streams extended by `ngtcp2_conn_extend_max_streams_uni`.
+ *
+ * Application which wishes to retransmit early data, it has to open
+ * streams and send stream data again.
*/
-NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn,
- ngtcp2_conn_stat *cstat);
+NGTCP2_EXTERN void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn);
/**
* @function
*
- * `ngtcp2_conn_on_loss_detection_timer` should be called when a timer
- * returned from `ngtcp2_conn_earliest_expiry` fires.
- *
- * Application should call `ngtcp2_conn_handshake` if handshake has
- * not completed, otherwise `ngtcp2_conn_write_pkt` (or
- * `ngtcp2_conn_write_stream` if it has data to send) to send PTO
- * probe packets.
- *
- * This function must not be called from inside the callback
- * functions.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * :macro:`NGTCP2_ERR_NOMEM`
- * Out of memory
+ * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to
+ * |*cstat|.
*/
-NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn,
- ngtcp2_tstamp ts);
+NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn,
+ int conn_stat_version,
+ ngtcp2_conn_stat *cstat);
/**
* @function
@@ -4300,9 +4842,8 @@ NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn,
* of length |datalen| to the library for transmission. The
* encryption level is given in |crypto_level|.
*
- * Application should keep the buffer pointed by |data| alive until
- * the data is acknowledged. The acknowledgement is notified by
- * :type:`ngtcp2_acked_crypto_offset` callback.
+ * The library makes a copy of the buffer pointed by |data| of length
+ * |datalen|. Application can discard |data|.
*/
NGTCP2_EXTERN int
ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
@@ -4333,7 +4874,7 @@ NGTCP2_EXTERN int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn,
* @function
*
* `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to
- * |conn|.
+ * the current path of |conn|.
*/
NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn,
const ngtcp2_addr *addr);
@@ -4341,11 +4882,11 @@ NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn,
/**
* @function
*
- * `ngtcp2_conn_set_remote_addr` sets remote endpoint address |addr|
- * to |conn|.
+ * `ngtcp2_conn_set_path_user_data` sets the |path_user_data| to the
+ * current path (see :member:`ngtcp2_path.user_data`).
*/
-NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn,
- const ngtcp2_addr *addr);
+NGTCP2_EXTERN void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn,
+ void *path_user_data);
/**
* @function
@@ -4357,20 +4898,70 @@ NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn);
/**
* @function
*
+ * `ngtcp2_conn_get_max_udp_payload_size` returns the maximum UDP
+ * payload size that this local endpoint would send. This is the
+ * value of :member:`ngtcp2_settings.max_udp_payload_size` that is
+ * passed to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_path_max_udp_payload_size` returns the maximum UDP
+ * payload size for the current path. If
+ * :member:`ngtcp2_settings.no_udp_payload_size_shaping` is set to
+ * nonzero, this function is equivalent to
+ * `ngtcp2_conn_get_max_udp_payload_size`. Otherwise, it returns the
+ * maximum UDP payload size that is probed for the current path.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_initiate_immediate_migration` starts connection
+ * migration to the given |path|.
+ * Only client can initiate migration. This function does
+ * immediate migration; it does not probe peer reachability from a new
+ * local address.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * Migration is disabled; or handshake is not yet confirmed; or
+ * client is migrating to server's preferred address.
+ * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED`
+ * No unused connection ID is available.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |local_addr| equals the current local address.
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ */
+NGTCP2_EXTERN int ngtcp2_conn_initiate_immediate_migration(
+ ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
* `ngtcp2_conn_initiate_migration` starts connection migration to the
- * given |path| which must not be ``NULL``. Only client can initiate
- * migration. This function does immediate migration; it does not
- * probe peer reachability from a new local address.
+ * given |path|. Only client can initiate migration. Unlike
+ * `ngtcp2_conn_initiate_immediate_migration`, this function starts a
+ * path validation with a new path and migrate to the new path after
+ * successful path validation.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`NGTCP2_ERR_INVALID_STATE`
- * Migration is disabled.
+ * Migration is disabled; or handshake is not yet confirmed; or
+ * client is migrating to server's preferred address.
* :macro:`NGTCP2_ERR_CONN_ID_BLOCKED`
* No unused connection ID is available.
* :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
- * |path| equals the current path.
+ * |local_addr| equals the current local address.
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory
*/
@@ -4397,6 +4988,16 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn);
/**
* @function
*
+ * `ngtcp2_conn_get_max_stream_data_left` returns the number of bytes
+ * that this local endpoint can send to a stream identified by
+ * |stream_id|. If no such stream is found, this function returns 0.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
* `ngtcp2_conn_get_streams_bidi_left` returns the number of
* bidirectional streams which the local endpoint can open without
* violating stream concurrency limit.
@@ -4415,6 +5016,15 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn);
/**
* @function
*
+ * `ngtcp2_conn_get_cwnd_left` returns the cwnd minus the number of
+ * bytes in flight on the current path. If the former is smaller than
+ * the latter, this function returns 0.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
* `ngtcp2_conn_set_initial_crypto_ctx` sets |ctx| for Initial packet
* encryption. The passed data will be passed to
* :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
@@ -4436,7 +5046,7 @@ ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn);
/**
* @function
*
- * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/Short packet
+ * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/1RTT packet
* encryption. The passed data will be passed to
* :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
* :type:`ngtcp2_hp_mask` callbacks.
@@ -4485,7 +5095,7 @@ ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead,
* @function
*
* `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx`
- * object for Handshake/Short packet encryption.
+ * object for Handshake/1RTT packet encryption.
*/
NGTCP2_EXTERN const ngtcp2_crypto_ctx *
ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn);
@@ -4528,33 +5138,213 @@ typedef enum ngtcp2_connection_close_error_code_type {
* indicates the error code is application error code.
*/
NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION,
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`
+ * is a special case of QUIC transport error, and it indicates that
+ * client receives Version Negotiation packet.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION,
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`
+ * is a special case of QUIC transport error, and it indicates that
+ * connection is closed because of idle timeout.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE
} ngtcp2_connection_close_error_code_type;
/**
* @struct
*
- * `ngtcp2_connection_close_error_code` contains connection error code
- * and its type.
+ * :type:`ngtcp2_connection_close_error` contains connection
+ * error code, its type, and the optional reason phrase.
*/
-typedef struct ngtcp2_connection_close_error_code {
+typedef struct ngtcp2_connection_close_error {
+ /**
+ * :member:`type` is the type of :member:`error_code`.
+ */
+ ngtcp2_connection_close_error_code_type type;
/**
* :member:`error_code` is the error code for connection closure.
*/
uint64_t error_code;
/**
- * :member:`type` is the type of :member:`error_code`.
+ * :member:`frame_type` is the type of QUIC frame which triggers
+ * this connection error. This field is set to 0 if the frame type
+ * is unknown.
*/
- ngtcp2_connection_close_error_code_type type;
-} ngtcp2_connection_close_error_code;
+ uint64_t frame_type;
+ /**
+ * :member:`reason` points to the buffer which contains a reason
+ * phrase. It may be NULL if there is no reason phrase. If it is
+ * received from a remote endpoint, it is truncated to at most 1024
+ * bytes.
+ */
+ uint8_t *reason;
+ /**
+ * :member:`reasonlen` is the length of data pointed by
+ * :member:`reason`.
+ */
+ size_t reasonlen;
+} ngtcp2_connection_close_error;
/**
* @function
*
- * `ngtcp2_conn_get_connection_close_error_code` stores the received
- * connection close error code in |ccec|.
+ * `ngtcp2_connection_close_error_default` initializes |ccerr| with
+ * the default values. It sets the following fields:
+ *
+ * - :member:`type <ngtcp2_connection_close_error.type>` =
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`
+ * - :member:`error_code <ngtcp2_connection_close_error.error_code>` =
+ * :macro:`NGTCP2_NO_ERROR`.
+ * - :member:`frame_type <ngtcp2_connection_close_error.frame_type>` =
+ * 0
+ * - :member:`reason <ngtcp2_connection_close_error.reason>` = NULL
+ * - :member:`reasonlen <ngtcp2_connection_close_error.reasonlen>` = 0
*/
-NGTCP2_EXTERN void ngtcp2_conn_get_connection_close_error_code(
- ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec);
+NGTCP2_EXTERN void
+ngtcp2_connection_close_error_default(ngtcp2_connection_close_error *ccerr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to |error_code|.
+ * |reason| is the reason phrase of length |reasonlen|. This function
+ * does not make a copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error_liberr` sets
+ * type and error_code based on |liberr|.
+ *
+ * If |liberr| is :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION`,
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` is set
+ * to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to
+ * :macro:`NGTCP2_NO_ERROR`. If |liberr| is
+ * :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->type
+ * <ngtcp2_connection_close_error.type>` is set to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to
+ * :macro:`NGTCP2_NO_ERROR`. Otherwise, :member:`ccerr->type
+ * <ngtcp2_connection_close_error.type>` is set to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` is set to an error code
+ * inferred by |liberr| (see
+ * `ngtcp2_err_infer_quic_transport_error_code`). |reason| is the
+ * reason phrase of length |reasonlen|. This function does not make a
+ * copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_liberr(
+ ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason,
+ size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error_tls_alert` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to bitwise-OR of
+ * :macro:`NGTCP2_CRYPTO_ERROR` and |tls_alert|. |reason| is the
+ * reason phrase of length |reasonlen|. This function does not make a
+ * copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_tls_alert(
+ ngtcp2_connection_close_error *ccerr, uint8_t tls_alert,
+ const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_application_error` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to |error_code|.
+ * |reason| is the reason phrase of length |reasonlen|. This function
+ * does not make a copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_application_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_connection_close` writes a packet which contains
+ * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed
+ * by |dest| whose capacity is |destlen|.
+ *
+ * For client, |destlen| should be at least
+ * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long. The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds. The metadata includes ECN markings.
+ *
+ * If :member:`ccerr->type <ngtcp2_connection_close_error.type>` ==
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * this function sends CONNECTION_CLOSE (type 0x1c) frame. If
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` ==
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`,
+ * it sends CONNECTION_CLOSE (type 0x1d) frame. Otherwise, it does
+ * not produce any data, and returns 0.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close. We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+ const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_connection_close_error` stores the received
+ * connection close error in |ccerr|.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_get_connection_close_error(ngtcp2_conn *conn,
+ ngtcp2_connection_close_error *ccerr);
/**
* @function
@@ -4600,10 +5390,45 @@ NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn,
/**
* @function
*
+ * `ngtcp2_conn_update_pkt_tx_time` sets the time instant of the next
+ * packet transmission. This function is noop if packet pacing is
+ * disabled. If packet pacing is enabled, this function must be
+ * called after (multiple invocation of) `ngtcp2_conn_writev_stream`.
+ * If packet aggregation (e.g., packet batching, GSO) is used, call
+ * this function after all aggregated datagrams are sent, which
+ * indicates multiple invocation of `ngtcp2_conn_writev_stream`.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_send_quantum` returns the maximum number of bytes
+ * that can be sent in one go without packet spacing. If packet
+ * pacing is disabled, this function returns SIZE_MAX.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_stream_loss_count` returns the number of packets
+ * that contain STREAM frame for a stream identified by |stream_id|
+ * and are declared to be lost. The number may include the spurious
+ * losses. If no stream identified by |stream_id| is found, this
+ * function returns 0.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
* `ngtcp2_strerror` returns the text representation of |liberr|.
* |liberr| must be one of ngtcp2 library error codes (which is
* defined as NGTCP2_ERR_* macro, such as
- * :macro:`NGTCP2_ERR_TLS_DECRYPT`).
+ * :macro:`NGTCP2_ERR_DECRYPT`).
*/
NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr);
@@ -4613,7 +5438,7 @@ NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr);
* `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error.
* |liberr| must be one of ngtcp2 library error codes (which is
* defined as NGTCP2_ERR_* macro, such as
- * :macro:`NGTCP2_ERR_TLS_DECRYPT`).
+ * :macro:`NGTCP2_ERR_DECRYPT`).
*/
NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr);
@@ -4623,7 +5448,7 @@ NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr);
* `ngtcp2_err_infer_quic_transport_error_code` returns a QUIC
* transport error code which corresponds to |liberr|. |liberr| must
* be one of ngtcp2 library error codes (which is defined as
- * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_TLS_DECRYPT`).
+ * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_DECRYPT`).
*/
NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr);
@@ -4634,8 +5459,22 @@ NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr);
* returns |dest|.
*/
NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest,
- const struct sockaddr *addr,
- size_t addrlen, void *user_data);
+ const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_addr_copy_byte` copies |addr| of length |addrlen| into the
+ * buffer pointed by :member:`dest->addr <ngtcp2_addr.addr>`.
+ * :member:`dest->addrlen <ngtcp2_addr.addrlen>` is updated to have
+ * |addrlen|. This function assumes that :member:`dest->addr
+ * <ngtcp2_addr.addr>` points to a buffer which has a sufficient
+ * capacity to store the copy.
+ */
+NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest,
+ const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen);
/**
* @function
@@ -4644,12 +5483,11 @@ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest,
* arguments. This function copies |local_addr| and |remote_addr|.
*/
NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps,
- const struct sockaddr *local_addr,
- size_t local_addrlen,
- void *local_user_data,
- const struct sockaddr *remote_addr,
- size_t remote_addrlen,
- void *remote_user_data);
+ const ngtcp2_sockaddr *local_addr,
+ ngtcp2_socklen local_addrlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen,
+ void *user_data);
/**
* @function
@@ -4671,8 +5509,13 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps);
* * :type:`initial_rtt <ngtcp2_settings.initial_rtt>` =
* :macro:`NGTCP2_DEFAULT_INITIAL_RTT`
* * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
+ * * :type:`max_udp_payload_size
+ * <ngtcp2_settings.max_udp_payload_size>` = 1452
+ * * :type:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
+ * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT`.
*/
-NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings);
+NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version,
+ ngtcp2_settings *settings);
/**
* @function
@@ -4683,7 +5526,7 @@ NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings);
*
* * :type:`max_udp_payload_size
* <ngtcp2_transport_params.max_udp_payload_size>` =
- * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE`
+ * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE`
* * :type:`ack_delay_exponent
* <ngtcp2_transport_params.ack_delay_exponent>` =
* :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT`
@@ -4694,7 +5537,8 @@ NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings);
* :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT`
*/
NGTCP2_EXTERN void
-ngtcp2_transport_params_default(ngtcp2_transport_params *params);
+ngtcp2_transport_params_default_versioned(int transport_params_version,
+ ngtcp2_transport_params *params);
/**
* @function
@@ -4746,13 +5590,14 @@ typedef struct ngtcp2_info {
/**
* @function
*
- * Returns a pointer to a ngtcp2_info struct with version information
- * about the run-time library in use. The |least_version| argument
- * can be set to a 24 bit numerical value for the least accepted
- * version number and if the condition is not met, this function will
- * return a ``NULL``. Pass in 0 to skip the version checking.
+ * `ngtcp2_version` returns a pointer to a ngtcp2_info struct with
+ * version information about the run-time library in use. The
+ * |least_version| argument can be set to a 24 bit numerical value for
+ * the least accepted version number and if the condition is not met,
+ * this function will return a ``NULL``. Pass in 0 to skip the
+ * version checking.
*/
-NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version);
+NGTCP2_EXTERN const ngtcp2_info *ngtcp2_version(int least_version);
/**
* @function
@@ -4763,66 +5608,211 @@ NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version);
NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id);
/**
- * @enum
+ * @function
*
- * :type:`ngtcp2_log_event` defines an event of ngtcp2 library
- * internal logger.
+ * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes
+ * that |dest| has enough buffer to store the deep copy of
+ * :member:`src->local <ngtcp2_path.local>` and :member:`src->remote
+ * <ngtcp2_path.remote>`.
*/
-typedef enum {
- /**
- * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event.
- */
- NGTCP2_LOG_EVENT_NONE,
- /**
- * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event
- */
- NGTCP2_LOG_EVENT_CON,
- /**
- * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event.
- */
- NGTCP2_LOG_EVENT_PKT,
- /**
- * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event.
- */
- NGTCP2_LOG_EVENT_FRM,
- /**
- * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event.
- */
- NGTCP2_LOG_EVENT_RCV,
- /**
- * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event.
- */
- NGTCP2_LOG_EVENT_CRY,
- /**
- * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event.
- */
- NGTCP2_LOG_EVENT_PTV,
-} ngtcp2_log_event;
+NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src);
/**
* @function
*
- * `ngtcp2_log_info` writes info level log.
+ * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same
+ * local and remote addresses.
*/
-NGTCP2_EXTERN void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev,
- const char *fmt, ...);
+NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b);
/**
* @function
*
- * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes
- * that |dest| has enough buffer to store the deep copy of src->local
- * and src->remote.
+ * `ngtcp2_is_supported_version` returns nonzero if the library supports
+ * QUIC version |version|.
*/
-NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src);
+NGTCP2_EXTERN int ngtcp2_is_supported_version(uint32_t version);
+
+/*
+ * @function
+ *
+ * `ngtcp2_is_reserved_version` returns nonzero if |version| is a
+ * reserved version.
+ */
+NGTCP2_EXTERN int ngtcp2_is_reserved_version(uint32_t version);
/**
* @function
*
- * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same
- * local and remote addresses.
+ * `ngtcp2_select_version` selects and returns a version from the
+ * version set |offered_versions| of |offered_versionslen| elements.
+ * |preferred_versions| of |preferred_versionslen| elements specifies
+ * the preference of versions, which is sorted in the order of
+ * preference. All versions included in |preferred_versions| must be
+ * supported by the library, that is, passing a version to
+ * `ngtcp2_is_supported_version` must return nonzero. This function
+ * is intended to be used by client when it receives Version
+ * Negotiation packet. If no version is selected, this function
+ * returns 0.
*/
-NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b);
+NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
+ size_t preferred_versionslen,
+ const uint32_t *offered_versions,
+ size_t offered_versionslen);
+
+/*
+ * Versioned function wrappers
+ */
+
+/*
+ * `ngtcp2_conn_read_pkt` is a wrapper around
+ * `ngtcp2_conn_read_pkt_versioned` to set the correct struct version.
+ */
+#define ngtcp2_conn_read_pkt(CONN, PATH, PI, PKT, PKTLEN, TS) \
+ ngtcp2_conn_read_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \
+ (PI), (PKT), (PKTLEN), (TS))
+
+/*
+ * `ngtcp2_conn_write_pkt` is a wrapper around
+ * `ngtcp2_conn_write_pkt_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_write_pkt(CONN, PATH, PI, DEST, DESTLEN, TS) \
+ ngtcp2_conn_write_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \
+ (PI), (DEST), (DESTLEN), (TS))
+
+/*
+ * `ngtcp2_conn_write_stream` is a wrapper around
+ * `ngtcp2_conn_write_stream_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_write_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \
+ FLAGS, STREAM_ID, DATA, DATALEN, TS) \
+ ngtcp2_conn_write_stream_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (PDATALEN), (FLAGS), (STREAM_ID), (DATA), (DATALEN), (TS))
+
+/*
+ * `ngtcp2_conn_writev_stream` is a wrapper around
+ * `ngtcp2_conn_writev_stream_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_writev_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \
+ FLAGS, STREAM_ID, DATAV, DATAVCNT, TS) \
+ ngtcp2_conn_writev_stream_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (PDATALEN), (FLAGS), (STREAM_ID), (DATAV), (DATAVCNT), (TS))
+
+/*
+ * `ngtcp2_conn_writev_datagram` is a wrapper around
+ * `ngtcp2_conn_writev_datagram_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_writev_datagram(CONN, PATH, PI, DEST, DESTLEN, PACCEPTED, \
+ FLAGS, DGRAM_ID, DATAV, DATAVCNT, TS) \
+ ngtcp2_conn_writev_datagram_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (PACCEPTED), (FLAGS), (DGRAM_ID), (DATAV), (DATAVCNT), (TS))
+
+/*
+ * `ngtcp2_conn_write_connection_close` is a wrapper around
+ * `ngtcp2_conn_write_connection_close_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_conn_write_connection_close(CONN, PATH, PI, DEST, DESTLEN, \
+ CCERR, TS) \
+ ngtcp2_conn_write_connection_close_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (CCERR), (TS))
+
+/*
+ * `ngtcp2_encode_transport_params` is a wrapper around
+ * `ngtcp2_encode_transport_params_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_encode_transport_params(DEST, DESTLEN, EXTTYPE, PARAMS) \
+ ngtcp2_encode_transport_params_versioned( \
+ (DEST), (DESTLEN), (EXTTYPE), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS))
+
+/*
+ * `ngtcp2_decode_transport_params` is a wrapper around
+ * `ngtcp2_decode_transport_params_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_decode_transport_params(PARAMS, EXTTYPE, DATA, DATALEN) \
+ ngtcp2_decode_transport_params_versioned( \
+ NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (EXTTYPE), (DATA), (DATALEN))
+
+/*
+ * `ngtcp2_conn_client_new` is a wrapper around
+ * `ngtcp2_conn_client_new_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_client_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \
+ SETTINGS, PARAMS, MEM, USER_DATA) \
+ ngtcp2_conn_client_new_versioned( \
+ (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \
+ (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \
+ NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA))
+
+/*
+ * `ngtcp2_conn_server_new` is a wrapper around
+ * `ngtcp2_conn_server_new_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_server_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \
+ SETTINGS, PARAMS, MEM, USER_DATA) \
+ ngtcp2_conn_server_new_versioned( \
+ (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \
+ (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \
+ NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA))
+
+/*
+ * `ngtcp2_conn_set_early_remote_transport_params` is a wrapper around
+ * `ngtcp2_conn_set_early_remote_transport_params_versioned` to set
+ * the correct struct version.
+ */
+#define ngtcp2_conn_set_early_remote_transport_params(CONN, PARAMS) \
+ ngtcp2_conn_set_early_remote_transport_params_versioned( \
+ (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS))
+
+/*
+ * `ngtcp2_conn_set_local_transport_params` is a wrapper around
+ * `ngtcp2_conn_set_local_transport_params_versioned` to set the
+ * correct struct version.
+ */
+#define ngtcp2_conn_set_local_transport_params(CONN, PARAMS) \
+ ngtcp2_conn_set_local_transport_params_versioned( \
+ (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS))
+
+/*
+ * `ngtcp2_transport_params_default` is a wrapper around
+ * `ngtcp2_transport_params_default_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_transport_params_default(PARAMS) \
+ ngtcp2_transport_params_default_versioned(NGTCP2_TRANSPORT_PARAMS_VERSION, \
+ (PARAMS))
+
+/*
+ * `ngtcp2_conn_get_conn_stat` is a wrapper around
+ * `ngtcp2_conn_get_conn_stat_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_get_conn_stat(CONN, CSTAT) \
+ ngtcp2_conn_get_conn_stat_versioned((CONN), NGTCP2_CONN_STAT_VERSION, (CSTAT))
+
+/*
+ * `ngtcp2_settings_default` is a wrapper around
+ * `ngtcp2_settings_default_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_settings_default(SETTINGS) \
+ ngtcp2_settings_default_versioned(NGTCP2_SETTINGS_VERSION, (SETTINGS))
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
#ifdef __cplusplus
}
diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h
index fb0671de9fc..9f7592b84a4 100644
--- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h
+++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h
@@ -36,7 +36,7 @@
*
* Version number of the ngtcp2 library release.
*/
-#define NGTCP2_VERSION "0.1.0-DEV"
+#define NGTCP2_VERSION "0.8.1"
/**
* @macro
@@ -46,6 +46,6 @@
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
* becomes 0x010203.
*/
-#define NGTCP2_VERSION_NUM 0x000100
+#define NGTCP2_VERSION_NUM 0x000801
#endif /* VERSION_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c
index 7a7f3e469a2..02c45de90d1 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c
@@ -26,22 +26,31 @@
#include <assert.h>
-int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
- ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) {
- *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry));
+#include "ngtcp2_macro.h"
+
+static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num,
+ ngtcp2_tstamp tstamp) {
+ ent->pkt_num = pkt_num;
+ ent->len = 1;
+ ent->tstamp = tstamp;
+}
+
+int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
+ ngtcp2_tstamp tstamp,
+ ngtcp2_objalloc *objalloc) {
+ *ent = ngtcp2_objalloc_acktr_entry_get(objalloc);
if (*ent == NULL) {
return NGTCP2_ERR_NOMEM;
}
- (*ent)->pkt_num = pkt_num;
- (*ent)->len = 1;
- (*ent)->tstamp = tstamp;
+ acktr_entry_init(*ent, pkt_num, tstamp);
return 0;
}
-void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) {
- ngtcp2_mem_free(mem, ent);
+void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
+ ngtcp2_objalloc *objalloc) {
+ ngtcp2_objalloc_acktr_entry_release(objalloc, ent);
}
static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
@@ -52,17 +61,15 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
const ngtcp2_mem *mem) {
int rv;
- rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry),
+ ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, 32, mem);
+
+ rv = ngtcp2_ringbuf_init(&acktr->acks, 32, sizeof(ngtcp2_acktr_ack_entry),
mem);
if (rv != 0) {
return rv;
}
- rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
- if (rv != 0) {
- ngtcp2_ringbuf_free(&acktr->acks);
- return rv;
- }
+ ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
acktr->log = log;
acktr->mem = mem;
@@ -74,24 +81,31 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
}
void ngtcp2_acktr_free(ngtcp2_acktr *acktr) {
+#ifdef NOMEMPOOL
ngtcp2_ksl_it it;
+#endif /* NOMEMPOOL */
if (acktr == NULL) {
return;
}
+#ifdef NOMEMPOOL
for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it);
ngtcp2_ksl_it_next(&it)) {
- ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem);
+ ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc);
}
+#endif /* NOMEMPOOL */
+
ngtcp2_ksl_free(&acktr->ents);
ngtcp2_ringbuf_free(&acktr->acks);
+
+ ngtcp2_objalloc_free(&acktr->objalloc);
}
int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
ngtcp2_tstamp ts) {
- ngtcp2_ksl_it it;
+ ngtcp2_ksl_it it, prev_it;
ngtcp2_acktr_entry *ent, *prev_ent, *delent;
int rv;
int added = 0;
@@ -122,16 +136,17 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
added = 1;
}
} else {
- ngtcp2_ksl_it_prev(&it);
- prev_ent = ngtcp2_ksl_it_get(&it);
+ prev_it = it;
+ ngtcp2_ksl_it_prev(&prev_it);
+ prev_ent = ngtcp2_ksl_it_get(&prev_it);
assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len);
if (ent->pkt_num + 1 == pkt_num) {
if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) {
prev_ent->len += ent->len + 1;
- ngtcp2_ksl_remove(&acktr->ents, NULL, &ent->pkt_num);
- ngtcp2_acktr_entry_del(ent, acktr->mem);
+ ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &ent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
added = 1;
} else {
ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num);
@@ -149,13 +164,13 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
}
if (!added) {
- rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem);
+ rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc);
if (rv != 0) {
return rv;
}
rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent);
if (rv != 0) {
- ngtcp2_acktr_entry_del(ent, acktr->mem);
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
return rv;
}
}
@@ -171,8 +186,8 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
it = ngtcp2_ksl_end(&acktr->ents);
ngtcp2_ksl_it_prev(&it);
delent = ngtcp2_ksl_it_get(&it);
- ngtcp2_ksl_remove(&acktr->ents, NULL, &delent->pkt_num);
- ngtcp2_acktr_entry_del(delent, acktr->mem);
+ ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &delent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc);
}
return 0;
@@ -186,8 +201,8 @@ void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) {
for (; !ngtcp2_ksl_it_end(&it);) {
ent = ngtcp2_ksl_it_get(&it);
- ngtcp2_ksl_remove(&acktr->ents, &it, &ent->pkt_num);
- ngtcp2_acktr_entry_del(ent, acktr->mem);
+ ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
}
}
@@ -212,13 +227,14 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
}
/*
- * acktr_remove removes |ent| from |acktr|. The iterator which points
- * to the entry next to |ent| is assigned to |it|.
+ * acktr_remove removes |ent| from |acktr|. |it| must point to the
+ * node whose key identifies |ent|. The iterator which points to the
+ * entry next to |ent| is assigned to |it|.
*/
static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it,
ngtcp2_acktr_entry *ent) {
- ngtcp2_ksl_remove(&acktr->ents, it, &ent->pkt_num);
- ngtcp2_acktr_entry_del(ent, acktr->mem);
+ ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
}
static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
@@ -310,7 +326,8 @@ void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) {
int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr,
ngtcp2_duration max_ack_delay,
ngtcp2_tstamp ts) {
- return acktr->first_unacked_ts <= ts - max_ack_delay;
+ return acktr->first_unacked_ts != UINT64_MAX &&
+ acktr->first_unacked_ts + max_ack_delay <= ts;
}
void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) {
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h
index 51e1588e3c4..1b00d64fe62 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h
@@ -35,6 +35,7 @@
#include "ngtcp2_ringbuf.h"
#include "ngtcp2_ksl.h"
#include "ngtcp2_pkt.h"
+#include "ngtcp2_objalloc.h"
/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry
which ngtcp2_acktr stores. */
@@ -46,22 +47,30 @@ typedef struct ngtcp2_log ngtcp2_log;
* ngtcp2_acktr_entry is a range of packets which need to be acked.
*/
typedef struct ngtcp2_acktr_entry {
- /* pkt_num is the largest packet number to acknowledge in this
- range. */
- int64_t pkt_num;
- /* len is the consecutive packets started from pkt_num which
- includes pkt_num itself counting in decreasing order. So pkt_num
- = 987 and len = 2, this entry includes packet 987 and 986. */
- size_t len;
- /* tstamp is the timestamp when a packet denoted by pkt_num is
- received. */
- ngtcp2_tstamp tstamp;
+ union {
+ struct {
+ /* pkt_num is the largest packet number to acknowledge in this
+ range. */
+ int64_t pkt_num;
+ /* len is the consecutive packets started from pkt_num which
+ includes pkt_num itself counting in decreasing order. So pkt_num
+ = 987 and len = 2, this entry includes packet 987 and 986. */
+ size_t len;
+ /* tstamp is the timestamp when a packet denoted by pkt_num is
+ received. */
+ ngtcp2_tstamp tstamp;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
} ngtcp2_acktr_entry;
+ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent);
+
/*
- * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it
- * with the given parameters. The pointer to the allocated object is
- * stored to |*ent|.
+ * ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and
+ * initializes it with the given parameters. The pointer to the
+ * allocated object is stored to |*ent|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -69,14 +78,16 @@ typedef struct ngtcp2_acktr_entry {
* NGTCP2_ERR_NOMEM
* Out of memory.
*/
-int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
- ngtcp2_tstamp tstamp, const ngtcp2_mem *mem);
+int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
+ ngtcp2_tstamp tstamp,
+ ngtcp2_objalloc *objalloc);
/*
- * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It
- * deallocates memory pointed by |ent|.
+ * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for
+ * |ent|.
*/
-void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem);
+void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
+ ngtcp2_objalloc *objalloc);
typedef struct ngtcp2_acktr_ack_entry {
/* largest_ack is the largest packet number in outgoing ACK frame */
@@ -86,25 +97,22 @@ typedef struct ngtcp2_acktr_ack_entry {
} ngtcp2_acktr_ack_entry;
/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */
-#define NGTCP2_ACKTR_FLAG_NONE 0x00
+#define NGTCP2_ACKTR_FLAG_NONE 0x00u
/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate
acknowledgement is required. */
-#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01
+#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01u
/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending
protected packet to be acknowledged. */
-#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02
-/* NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK is set when server received
- acknowledgement for ACK which acknowledges the last handshake
- packet from client (which contains TLSv1.3 Finished message). */
-#define NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK 0x80
+#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02u
/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
expired and canceled. */
-#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100
+#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100u
/*
* ngtcp2_acktr tracks received packets which we have to send ack.
*/
typedef struct ngtcp2_acktr {
+ ngtcp2_objalloc objalloc;
ngtcp2_ringbuf acks;
/* ents includes ngtcp2_acktr_entry sorted by decreasing order of
packet number. */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c
index 22af219a9e1..daab5dd7ce6 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c
@@ -27,55 +27,41 @@
#include <string.h>
#include <assert.h>
-#ifdef WIN32
-# include <winsock2.h>
-# include <ws2tcpip.h>
-#else
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <netinet/ip.h>
-# include <netinet/tcp.h>
-# include <arpa/inet.h>
-#endif
-
-ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr,
- size_t addrlen, void *user_data) {
+ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen) {
dest->addrlen = addrlen;
- dest->addr = (struct sockaddr *)addr;
- dest->user_data = user_data;
+ dest->addr = (ngtcp2_sockaddr *)addr;
return dest;
}
void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) {
dest->addrlen = src->addrlen;
if (src->addrlen) {
- memcpy(dest->addr, src->addr, src->addrlen);
+ memcpy(dest->addr, src->addr, (size_t)src->addrlen);
}
- dest->user_data = src->user_data;
}
-void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr,
- size_t addrlen) {
+void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen) {
dest->addrlen = addrlen;
if (addrlen) {
- memcpy(dest->addr, addr, addrlen);
+ memcpy(dest->addr, addr, (size_t)addrlen);
}
}
-static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) {
+static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
assert(a->sa_family == b->sa_family);
switch (a->sa_family) {
- case AF_INET: {
- const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a,
- *bi = (const struct sockaddr_in *)(void *)b;
+ case NGTCP2_AF_INET: {
+ const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in *)(void *)b;
return ai->sin_port == bi->sin_port &&
memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0;
}
- case AF_INET6: {
- const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a,
- *bi = (const struct sockaddr_in6 *)(void *)b;
+ case NGTCP2_AF_INET6: {
+ const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
return ai->sin6_port == bi->sin6_port &&
memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
}
@@ -92,17 +78,17 @@ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) {
uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE;
- const struct sockaddr *a = aa->addr;
- const struct sockaddr *b = bb->addr;
+ const ngtcp2_sockaddr *a = aa->addr;
+ const ngtcp2_sockaddr *b = bb->addr;
if (a->sa_family != b->sa_family) {
return NGTCP2_ADDR_COMPARE_FLAG_FAMILY;
}
switch (a->sa_family) {
- case AF_INET: {
- const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a,
- *bi = (const struct sockaddr_in *)(void *)b;
+ case NGTCP2_AF_INET: {
+ const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in *)(void *)b;
if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
}
@@ -111,9 +97,9 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
}
return flags;
}
- case AF_INET6: {
- const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a,
- *bi = (const struct sockaddr_in6 *)(void *)b;
+ case NGTCP2_AF_INET6: {
+ const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h
index 84c0c01ddbf..f1d7f7bdc70 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h
@@ -39,29 +39,20 @@
void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src);
/*
- * ngtcp2_addr_copy_byte copies |addr| of length |addrlen| into the
- * buffer pointed by dest->addr. dest->len is updated to have
- * |addrlen|. This function assumes that dest->addr points to a
- * buffer which have sufficient size to store the copy.
- */
-void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr,
- size_t addrlen);
-
-/*
* ngtcp2_addr_eq returns nonzero if |a| equals |b|.
*/
int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b);
/* NGTCP2_ADDR_COMPARE_FLAG_NONE indicates that no flag set. */
-#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0
+#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0u
/* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not
match. */
-#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1
+#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1u
/* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */
-#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2
+#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2u
/* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not
match. */
-#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4
+#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4u
/*
* ngtcp2_addr_compare compares address and port between |a| and |b|,
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c
new file mode 100644
index 00000000000..5cc39ee3b03
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c
@@ -0,0 +1,90 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_balloc.h"
+
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+
+void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
+ const ngtcp2_mem *mem) {
+ assert((blklen & 0xfu) == 0);
+
+ balloc->mem = mem;
+ balloc->blklen = blklen;
+ balloc->head = NULL;
+ ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
+}
+
+void ngtcp2_balloc_free(ngtcp2_balloc *balloc) {
+ if (balloc == NULL) {
+ return;
+ }
+
+ ngtcp2_balloc_clear(balloc);
+}
+
+void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) {
+ ngtcp2_memblock_hd *p, *next;
+
+ for (p = balloc->head; p; p = next) {
+ next = p->next;
+ ngtcp2_mem_free(balloc->mem, p);
+ }
+
+ balloc->head = NULL;
+ ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
+}
+
+int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) {
+ uint8_t *p;
+ ngtcp2_memblock_hd *hd;
+
+ assert(n <= balloc->blklen);
+
+ if (ngtcp2_buf_left(&balloc->buf) < n) {
+ p = ngtcp2_mem_malloc(balloc->mem,
+ sizeof(ngtcp2_memblock_hd) + 0x10u + balloc->blklen);
+ if (p == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ hd = (ngtcp2_memblock_hd *)(void *)p;
+ hd->next = balloc->head;
+ balloc->head = hd;
+ ngtcp2_buf_init(
+ &balloc->buf,
+ (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfu) &
+ ~(uintptr_t)0xfu),
+ balloc->blklen);
+ }
+
+ assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
+
+ *pbuf = balloc->buf.last;
+ balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
+
+ return 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h
new file mode 100644
index 00000000000..1fb16325d99
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h
@@ -0,0 +1,91 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BALLOC_H
+#define NGTCP2_BALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_buf.h"
+
+typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd;
+
+/*
+ * ngtcp2_memblock_hd is the header of memory block.
+ */
+struct ngtcp2_memblock_hd {
+ ngtcp2_memblock_hd *next;
+};
+
+/*
+ * ngtcp2_balloc is a custom memory allocator. It allocates |blklen|
+ * bytes of memory at once on demand, and returns its slice when the
+ * allocation is requested.
+ */
+typedef struct ngtcp2_balloc {
+ /* mem is the underlying memory allocator. */
+ const ngtcp2_mem *mem;
+ /* blklen is the size of memory block. */
+ size_t blklen;
+ /* head points to the list of memory block allocated so far. */
+ ngtcp2_memblock_hd *head;
+ /* buf wraps the current memory block for allocation requests. */
+ ngtcp2_buf buf;
+} ngtcp2_balloc;
+
+/*
+ * ngtcp2_balloc_init initializes |balloc| with |blklen| which is the
+ * size of memory block.
+ */
+void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_balloc_free releases all allocated memory blocks.
+ */
+void ngtcp2_balloc_free(ngtcp2_balloc *balloc);
+
+/*
+ * ngtcp2_balloc_get allocates |n| bytes of memory and assigns its
+ * pointer to |*pbuf|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n);
+
+/*
+ * ngtcp2_balloc_clear releases all allocated memory blocks and
+ * initializes its state.
+ */
+void ngtcp2_balloc_clear(ngtcp2_balloc *balloc);
+
+#endif /* NGTCP2_BALLOC_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c
new file mode 100644
index 00000000000..0816d69b816
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c
@@ -0,0 +1,692 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_bbr.h"
+
+#include <assert.h>
+
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_rst.h"
+
+static const double pacing_gain_cycle[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
+
+#define NGTCP2_BBR_GAIN_CYCLELEN \
+ (sizeof(pacing_gain_cycle) / sizeof(pacing_gain_cycle[0]))
+
+#define NGTCP2_BBR_HIGH_GAIN 2.89
+#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS)
+#define NGTCP2_RTPROP_FILTERLEN (10 * NGTCP2_SECONDS)
+#define NGTCP2_BBR_BTL_BW_FILTERLEN 10
+
+static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_init_round_counting(ngtcp2_bbr_cc *cc);
+static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack);
+static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain);
+static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat);
+static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts);
+static void bbr_enter_startup(ngtcp2_bbr_cc *cc);
+static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc);
+static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc);
+static void bbr_enter_drain(ngtcp2_bbr_cc *cc);
+static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
+static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
+static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat);
+static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc);
+static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
+
+void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
+ ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
+ ngtcp2_log *log) {
+ cc->ccb.log = log;
+ cc->rst = rst;
+ cc->rand = rand;
+ cc->rand_ctx = *rand_ctx;
+ cc->initial_cwnd = cstat->cwnd;
+ bbr_init(cc, cstat, initial_ts);
+}
+
+void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc) { (void)cc; }
+
+int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem) {
+ ngtcp2_bbr_cc *bbr_cc;
+
+ bbr_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr_cc));
+ if (bbr_cc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_bbr_cc_init(bbr_cc, cstat, rst, initial_ts, rand, rand_ctx, log);
+
+ cc->ccb = &bbr_cc->ccb;
+ cc->on_pkt_acked = ngtcp2_cc_bbr_cc_on_pkt_acked;
+ cc->congestion_event = ngtcp2_cc_bbr_cc_congestion_event;
+ cc->on_spurious_congestion = ngtcp2_cc_bbr_cc_on_spurious_congestion;
+ cc->on_persistent_congestion = ngtcp2_cc_bbr_cc_on_persistent_congestion;
+ cc->on_ack_recv = ngtcp2_cc_bbr_cc_on_ack_recv;
+ cc->on_pkt_sent = ngtcp2_cc_bbr_cc_on_pkt_sent;
+ cc->new_rtt_sample = ngtcp2_cc_bbr_cc_new_rtt_sample;
+ cc->reset = ngtcp2_cc_bbr_cc_reset;
+ cc->event = ngtcp2_cc_bbr_cc_event;
+
+ return 0;
+}
+
+void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr_cc, ccb);
+
+ ngtcp2_bbr_cc_free(bbr_cc);
+ ngtcp2_mem_free(mem, bbr_cc);
+}
+
+void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)pkt;
+ (void)ts;
+}
+
+static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_time) {
+ return cstat->congestion_recovery_start_ts != UINT64_MAX &&
+ sent_time <= cstat->congestion_recovery_start_ts;
+}
+
+void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+
+ if (cc->in_loss_recovery || cc->congestion_recovery_start_ts != UINT64_MAX ||
+ in_congestion_recovery(cstat, sent_ts)) {
+ return;
+ }
+
+ cc->congestion_recovery_start_ts = ts;
+}
+
+void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ (void)ts;
+
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ if (cc->in_loss_recovery) {
+ cc->in_loss_recovery = 0;
+ cc->packet_conservation = 0;
+ bbr_restore_cwnd(cc, cstat);
+ }
+}
+
+void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ (void)ts;
+
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cc->in_loss_recovery = 0;
+ cc->packet_conservation = 0;
+
+ bbr_save_cwnd(cc, cstat);
+ cstat->cwnd = 2 * cstat->max_udp_payload_size;
+}
+
+void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+
+ bbr_update_on_ack(bbr_cc, cstat, ack, ts);
+}
+
+void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ (void)pkt;
+
+ bbr_on_transmit(bbr_cc, cstat);
+}
+
+void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)ts;
+}
+
+void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ bbr_init(bbr_cc, cstat, ts);
+}
+
+void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)event;
+ (void)ts;
+}
+
+static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ bbr_update_model_and_state(cc, cstat, ack, ts);
+ bbr_update_control_parameters(cc, cstat, ack);
+}
+
+static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ bbr_update_btl_bw(cc, cstat, ack);
+ bbr_check_cycle_phase(cc, cstat, ack, ts);
+ bbr_check_full_pipe(cc);
+ bbr_check_drain(cc, cstat, ts);
+ bbr_update_rtprop(cc, cstat, ts);
+ bbr_check_probe_rtt(cc, cstat, ts);
+}
+
+static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_set_pacing_rate(cc, cstat);
+ bbr_set_send_quantum(cc, cstat);
+ bbr_set_cwnd(cc, cstat, ack);
+}
+
+static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ bbr_handle_restart_from_idle(cc, cstat);
+}
+
+static void bbr_init_round_counting(ngtcp2_bbr_cc *cc) {
+ cc->next_round_delivered = 0;
+ cc->round_start = 0;
+ cc->round_count = 0;
+}
+
+static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) {
+ if (ack->pkt_delivered >= cc->next_round_delivered) {
+ cc->next_round_delivered = cc->rst->delivered;
+ ++cc->round_count;
+ cc->round_start = 1;
+
+ return;
+ }
+
+ cc->round_start = 0;
+}
+
+static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (cc->in_loss_recovery) {
+ if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) {
+ cc->packet_conservation = 0;
+ }
+
+ if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) {
+ cc->in_loss_recovery = 0;
+ cc->packet_conservation = 0;
+ bbr_restore_cwnd(cc, cstat);
+ }
+
+ return;
+ }
+
+ if (cc->congestion_recovery_start_ts != UINT64_MAX) {
+ cc->in_loss_recovery = 1;
+ bbr_save_cwnd(cc, cstat);
+ cstat->cwnd = cstat->bytes_in_flight +
+ ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size);
+
+ cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts;
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cc->packet_conservation = 1;
+ cc->congestion_recovery_next_round_delivered = cc->rst->delivered;
+ }
+}
+
+static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_round(cc, ack);
+ bbr_handle_recovery(cc, cstat, ack);
+
+ if (cstat->delivery_rate_sec < cc->btl_bw && cc->rst->rs.is_app_limited) {
+ return;
+ }
+
+ ngtcp2_window_filter_update(&cc->btl_bw_filter, cstat->delivery_rate_sec,
+ cc->round_count);
+
+ cc->btl_bw = ngtcp2_window_filter_get_best(&cc->btl_bw_filter);
+}
+
+static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ cc->rtprop_expired = ts > cc->rtprop_stamp + NGTCP2_RTPROP_FILTERLEN;
+
+ /* Need valid RTT sample */
+ if (cstat->latest_rtt &&
+ (cstat->latest_rtt <= cc->rt_prop || cc->rtprop_expired)) {
+ cc->rt_prop = cstat->latest_rtt;
+ cc->rtprop_stamp = ts;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr update RTprop=%" PRIu64, cc->rt_prop);
+ }
+}
+
+static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ double nominal_bandwidth =
+ (double)cc->initial_cwnd / (double)NGTCP2_MILLISECONDS;
+
+ cstat->pacing_rate = cc->pacing_gain * nominal_bandwidth;
+}
+
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain) {
+ double rate = pacing_gain * (double)cc->btl_bw / NGTCP2_SECONDS;
+
+ if (cc->filled_pipe || rate > cstat->pacing_rate) {
+ cstat->pacing_rate = rate;
+ }
+}
+
+static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ bbr_set_pacing_rate_with_gain(cc, cstat, cc->pacing_gain);
+}
+
+static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ uint64_t send_quantum;
+ (void)cc;
+
+ if (cstat->pacing_rate < 1.2 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
+ cstat->send_quantum = cstat->max_udp_payload_size;
+ } else if (cstat->pacing_rate < 24.0 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
+ cstat->send_quantum = cstat->max_udp_payload_size * 2;
+ } else {
+ send_quantum =
+ (uint64_t)(cstat->pacing_rate * (double)(cstat->min_rtt == UINT64_MAX
+ ? NGTCP2_MILLISECONDS
+ : cstat->min_rtt));
+ cstat->send_quantum = (size_t)ngtcp2_min(send_quantum, 64 * 1024);
+ }
+
+ cstat->send_quantum =
+ ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10);
+}
+
+static uint64_t bbr_inflight(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ double gain) {
+ uint64_t quanta = 3 * cstat->send_quantum;
+ double estimated_bdp;
+
+ if (cc->rt_prop == UINT64_MAX) {
+ /* no valid RTT samples yet */
+ return cc->initial_cwnd;
+ }
+
+ estimated_bdp = (double)cc->btl_bw * (double)cc->rt_prop / NGTCP2_SECONDS;
+
+ return (uint64_t)(gain * estimated_bdp) + quanta;
+}
+
+static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ cc->target_cwnd = bbr_inflight(cc, cstat, cc->cwnd_gain);
+}
+
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (ack->bytes_lost > 0) {
+ if (cstat->cwnd > ack->bytes_lost) {
+ cstat->cwnd -= ack->bytes_lost;
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size);
+ } else {
+ cstat->cwnd = cstat->max_udp_payload_size;
+ }
+ }
+
+ if (cc->packet_conservation) {
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered);
+ }
+}
+
+static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ if (!cc->in_loss_recovery && cc->state != NGTCP2_BBR_STATE_PROBE_RTT) {
+ cc->prior_cwnd = cstat->cwnd;
+ return;
+ }
+
+ cc->prior_cwnd = ngtcp2_max(cc->prior_cwnd, cstat->cwnd);
+}
+
+static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, cc->prior_cwnd);
+}
+
+static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) {
+ return max_udp_payload_size * 4;
+}
+
+static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat) {
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
+ cstat->cwnd =
+ ngtcp2_min(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size));
+ }
+}
+
+static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_target_cwnd(cc, cstat);
+ bbr_modulate_cwnd_for_recovery(cc, cstat, ack);
+
+ if (!cc->packet_conservation) {
+ if (cc->filled_pipe) {
+ cstat->cwnd =
+ ngtcp2_min(cstat->cwnd + ack->bytes_delivered, cc->target_cwnd);
+ } else if (cstat->cwnd < cc->target_cwnd ||
+ cc->rst->delivered < cc->initial_cwnd) {
+ cstat->cwnd += ack->bytes_delivered;
+ }
+
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size));
+ }
+
+ bbr_modulate_cwnd_for_probe_rtt(cc, cstat);
+}
+
+static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts) {
+ cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
+ cc->prior_cwnd = 0;
+ cc->target_cwnd = 0;
+ cc->btl_bw = 0;
+ cc->rt_prop = UINT64_MAX;
+ cc->rtprop_stamp = initial_ts;
+ cc->cycle_stamp = UINT64_MAX;
+ cc->probe_rtt_done_stamp = UINT64_MAX;
+ cc->cycle_index = 0;
+ cc->rtprop_expired = 0;
+ cc->idle_restart = 0;
+ cc->packet_conservation = 0;
+ cc->probe_rtt_round_done = 0;
+
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cc->congestion_recovery_next_round_delivered = 0;
+ cc->in_loss_recovery = 0;
+
+ cstat->send_quantum = cstat->max_udp_payload_size * 10;
+
+ ngtcp2_window_filter_init(&cc->btl_bw_filter, NGTCP2_BBR_BTL_BW_FILTERLEN);
+
+ bbr_init_round_counting(cc);
+ bbr_init_full_pipe(cc);
+ bbr_init_pacing_rate(cc, cstat);
+ bbr_enter_startup(cc);
+}
+
+static void bbr_enter_startup(ngtcp2_bbr_cc *cc) {
+ cc->state = NGTCP2_BBR_STATE_STARTUP;
+ cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
+ cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
+}
+
+static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc) {
+ cc->filled_pipe = 0;
+ cc->full_bw = 0;
+ cc->full_bw_count = 0;
+}
+
+static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc) {
+ if (cc->filled_pipe || !cc->round_start || cc->rst->rs.is_app_limited) {
+ /* no need to check for a full pipe now. */
+ return;
+ }
+
+ /* cc->btl_bw still growing? */
+ if (cc->btl_bw * 100 >= cc->full_bw * 125) {
+ /* record new baseline level */
+ cc->full_bw = cc->btl_bw;
+ cc->full_bw_count = 0;
+ return;
+ }
+ /* another round w/o much growth */
+ ++cc->full_bw_count;
+ if (cc->full_bw_count >= 3) {
+ cc->filled_pipe = 1;
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr filled pipe, btl_bw=%" PRIu64, cc->btl_bw);
+ }
+}
+
+static void bbr_enter_drain(ngtcp2_bbr_cc *cc) {
+ cc->state = NGTCP2_BBR_STATE_DRAIN;
+ /* pace slowly */
+ cc->pacing_gain = 1.0 / NGTCP2_BBR_HIGH_GAIN;
+ /* maintain cwnd */
+ cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
+}
+
+static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (cc->state == NGTCP2_BBR_STATE_STARTUP && cc->filled_pipe) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit Startup and enter Drain");
+
+ bbr_enter_drain(cc);
+ }
+
+ if (cc->state == NGTCP2_BBR_STATE_DRAIN &&
+ cstat->bytes_in_flight <= bbr_inflight(cc, cstat, 1.0)) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit Drain and enter ProbeBW");
+
+ /* we estimate queue is drained */
+ bbr_enter_probe_bw(cc, ts);
+ }
+}
+
+static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
+ uint8_t rand;
+
+ cc->state = NGTCP2_BBR_STATE_PROBE_BW;
+ cc->pacing_gain = 1;
+ cc->cwnd_gain = 2;
+
+ assert(cc->rand);
+
+ cc->rand(&rand, 1, &cc->rand_ctx);
+
+ cc->cycle_index = NGTCP2_BBR_GAIN_CYCLELEN - 1 - (size_t)(rand * 7 / 256);
+ bbr_advance_cycle_phase(cc, ts);
+}
+
+static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_BW &&
+ bbr_is_next_cycle_phase(cc, cstat, ack, ts)) {
+ bbr_advance_cycle_phase(cc, ts);
+ }
+}
+
+static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
+ cc->cycle_stamp = ts;
+ cc->cycle_index = (cc->cycle_index + 1) & (NGTCP2_BBR_GAIN_CYCLELEN - 1);
+ cc->pacing_gain = pacing_gain_cycle[cc->cycle_index];
+}
+
+static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ int is_full_length = (ts - cc->cycle_stamp) > cc->rt_prop;
+
+ if (cc->pacing_gain > 1) {
+ return is_full_length && (ack->bytes_lost > 0 ||
+ ack->prior_bytes_in_flight >=
+ bbr_inflight(cc, cstat, cc->pacing_gain));
+ }
+
+ if (cc->pacing_gain < 1) {
+ return is_full_length ||
+ ack->prior_bytes_in_flight <= bbr_inflight(cc, cstat, 1);
+ }
+
+ return is_full_length;
+}
+
+static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat) {
+ if (cstat->bytes_in_flight == 0 && cc->rst->app_limited) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr restart from idle");
+
+ cc->idle_restart = 1;
+
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_BW) {
+ bbr_set_pacing_rate_with_gain(cc, cstat, 1);
+ }
+ }
+}
+
+static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (cc->state != NGTCP2_BBR_STATE_PROBE_RTT && cc->rtprop_expired &&
+ !cc->idle_restart) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr enter ProbeRTT");
+
+ bbr_enter_probe_rtt(cc);
+ bbr_save_cwnd(cc, cstat);
+ cc->probe_rtt_done_stamp = UINT64_MAX;
+ }
+
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
+ bbr_handle_probe_rtt(cc, cstat, ts);
+ }
+
+ cc->idle_restart = 0;
+}
+
+static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc) {
+ cc->state = NGTCP2_BBR_STATE_PROBE_RTT;
+ cc->pacing_gain = 1;
+ cc->cwnd_gain = 1;
+}
+
+static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ uint64_t app_limited = cc->rst->delivered + cstat->bytes_in_flight;
+
+ /* Ignore low rate samples during NGTCP2_BBR_STATE_PROBE_RTT. */
+ cc->rst->app_limited = app_limited ? app_limited : 1;
+
+ if (cc->probe_rtt_done_stamp == UINT64_MAX &&
+ cstat->bytes_in_flight <= min_pipe_cwnd(cstat->max_udp_payload_size)) {
+ cc->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION;
+ cc->probe_rtt_round_done = 0;
+ cc->next_round_delivered = cc->rst->delivered;
+
+ return;
+ }
+
+ if (cc->probe_rtt_done_stamp != UINT64_MAX) {
+ if (cc->round_start) {
+ cc->probe_rtt_round_done = 1;
+ }
+
+ if (cc->probe_rtt_round_done && ts > cc->probe_rtt_done_stamp) {
+ cc->rtprop_stamp = ts;
+ bbr_restore_cwnd(cc, cstat);
+ bbr_exit_probe_rtt(cc, ts);
+ }
+ }
+}
+
+static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
+ if (cc->filled_pipe) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit ProbeRTT and enter ProbeBW");
+
+ bbr_enter_probe_bw(cc, ts);
+
+ return;
+ }
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit ProbeRTT and enter Startup");
+
+ bbr_enter_startup(cc);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h
new file mode 100644
index 00000000000..7311f051e18
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h
@@ -0,0 +1,156 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BBR_H
+#define NGTCP2_BBR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_cc.h"
+#include "ngtcp2_window_filter.h"
+
+typedef struct ngtcp2_rst ngtcp2_rst;
+
+typedef enum ngtcp2_bbr_state {
+ NGTCP2_BBR_STATE_STARTUP,
+ NGTCP2_BBR_STATE_DRAIN,
+ NGTCP2_BBR_STATE_PROBE_BW,
+ NGTCP2_BBR_STATE_PROBE_RTT,
+} ngtcp2_bbr_state;
+
+/*
+ * ngtcp2_bbr_cc is BBR congestion controller, described in
+ * https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00
+ */
+typedef struct ngtcp2_bbr_cc {
+ ngtcp2_cc_base ccb;
+
+ /* The max filter used to estimate BBR.BtlBw. */
+ ngtcp2_window_filter btl_bw_filter;
+ uint64_t initial_cwnd;
+ ngtcp2_rst *rst;
+ ngtcp2_rand rand;
+ ngtcp2_rand_ctx rand_ctx;
+
+ /* BBR variables */
+
+ /* The dynamic gain factor used to scale BBR.BtlBw to
+ produce BBR.pacing_rate. */
+ double pacing_gain;
+ /* The dynamic gain factor used to scale the estimated BDP to produce a
+ congestion window (cwnd). */
+ double cwnd_gain;
+ uint64_t full_bw;
+ /* packet.delivered value denoting the end of a packet-timed round trip. */
+ uint64_t next_round_delivered;
+ /* Count of packet-timed round trips. */
+ uint64_t round_count;
+ uint64_t prior_cwnd;
+ /* target_cwnd is the upper bound on the volume of data BBR
+ allows in flight. */
+ uint64_t target_cwnd;
+ /* BBR's estimated bottleneck bandwidth available to the
+ transport flow, estimated from the maximum delivery rate sample in a
+ sliding window. */
+ uint64_t btl_bw;
+ /* BBR's estimated two-way round-trip propagation delay of
+ the path, estimated from the windowed minimum recent round-trip delay
+ sample. */
+ ngtcp2_duration rt_prop;
+ /* The wall clock time at which the current BBR.RTProp
+ sample was obtained. */
+ ngtcp2_tstamp rtprop_stamp;
+ ngtcp2_tstamp cycle_stamp;
+ ngtcp2_tstamp probe_rtt_done_stamp;
+ /* congestion_recovery_start_ts is the time when congestion recovery
+ period started.*/
+ ngtcp2_tstamp congestion_recovery_start_ts;
+ uint64_t congestion_recovery_next_round_delivered;
+ size_t full_bw_count;
+ size_t cycle_index;
+ ngtcp2_bbr_state state;
+ /* A boolean that records whether BBR estimates that it has ever fully
+ utilized its available bandwidth ("filled the pipe"). */
+ int filled_pipe;
+ /* A boolean that BBR sets to true once per packet-timed round trip,
+ on ACKs that advance BBR.round_count. */
+ int round_start;
+ int rtprop_expired;
+ int idle_restart;
+ int packet_conservation;
+ int probe_rtt_round_done;
+ /* in_loss_recovery becomes nonzero when BBR enters loss recovery
+ period. */
+ int in_loss_recovery;
+} ngtcp2_bbr_cc;
+
+int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *bbr_cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
+ ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
+ ngtcp2_log *log);
+
+void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc);
+
+void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt);
+
+void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
+
+#endif /* NGTCP2_BBR_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
new file mode 100644
index 00000000000..585ea11e8e2
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
@@ -0,0 +1,1486 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_bbr2.h"
+
+#include <assert.h>
+
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_rst.h"
+
+#define NGTCP2_BBR_MAX_BW_FILTERLEN 2
+
+#define NGTCP2_BBR_EXTRA_ACKED_FILTERLEN 10
+
+#define NGTCP2_BBR_STARTUP_PACING_GAIN ((double)2.77)
+
+#define NGTCP2_BBR_STARTUP_CWND_GAIN 2
+
+#define NGTCP2_BBR_PROBE_RTT_CWND_GAIN ((double)0.5)
+
+#define NGTCP2_BBR_BETA_NUMER 7
+#define NGTCP2_BBR_BETA_DENOM 10
+
+#define NGTCP2_BBR_LOSS_THRESH_NUMER 2
+#define NGTCP2_BBR_LOSS_THRESH_DENOM 100
+
+#define NGTCP2_BBR_HEADROOM_NUMER 15
+#define NGTCP2_BBR_HEADROOM_DENOM 100
+
+#define NGTCP2_BBR_PROBE_RTT_INTERVAL (5 * NGTCP2_SECONDS)
+#define NGTCP2_BBR_MIN_RTT_FILTERLEN (10 * NGTCP2_SECONDS)
+
+#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS)
+
+#define NGTCP2_BBR_PACING_MARGIN_PERCENT 1
+
+static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts);
+
+static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain);
+
+static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+static void bbr_update_model_and_state(ngtcp2_bbr2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static void bbr_update_control_parameters(ngtcp2_bbr2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
+
+static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack);
+
+static void bbr_start_round(ngtcp2_bbr2_cc *bbr);
+
+static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts);
+
+static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts);
+
+static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
+static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_duration interval, ngtcp2_tstamp ts);
+
+static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr);
+
+static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static int is_inflight_too_high(const ngtcp2_rs *rs);
+
+static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_rs *rs, ngtcp2_tstamp ts);
+
+static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
+
+static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_rs *rs,
+ const ngtcp2_cc_pkt *pkt);
+
+static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
+static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts);
+
+static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw, double gain);
+
+static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ uint64_t inflight);
+
+static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ uint64_t bw, double gain);
+
+static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static uint64_t min_pipe_cwnd(size_t max_udp_payload_size);
+
+static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_time);
+
+static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts) {
+ ngtcp2_window_filter_init(&bbr->max_bw_filter, NGTCP2_BBR_MAX_BW_FILTERLEN);
+ ngtcp2_window_filter_init(&bbr->extra_acked_filter,
+ NGTCP2_BBR_EXTRA_ACKED_FILTERLEN);
+
+ bbr->min_rtt = UINT64_MAX;
+ bbr->min_rtt_stamp = initial_ts;
+ /* remark: Use UINT64_MAX instead of 0 for consistency. */
+ bbr->probe_rtt_done_stamp = UINT64_MAX;
+ bbr->probe_rtt_round_done = 0;
+ bbr->prior_cwnd = 0;
+ bbr->idle_restart = 0;
+ bbr->extra_acked_interval_start = initial_ts;
+ bbr->extra_acked_delivered = 0;
+
+ bbr_reset_congestion_signals(bbr);
+ bbr_reset_lower_bounds(bbr);
+ bbr_init_round_counting(bbr);
+ bbr_init_full_pipe(bbr);
+ bbr_init_pacing_rate(bbr, cstat);
+ bbr_enter_startup(bbr);
+
+ cstat->send_quantum = cstat->max_udp_payload_size * 10;
+
+ /* Missing in documentation */
+ bbr->loss_round_start = 0;
+ bbr->loss_round_delivered = UINT64_MAX;
+
+ bbr->rounds_since_bw_probe = 0;
+
+ bbr->max_bw = 0;
+ bbr->bw = 0;
+
+ bbr->cycle_count = 0;
+
+ bbr->extra_acked = 0;
+
+ bbr->bytes_lost_in_round = 0;
+ bbr->loss_events_in_round = 0;
+
+ bbr->offload_budget = 0;
+
+ bbr->probe_up_cnt = UINT64_MAX;
+ bbr->cycle_stamp = UINT64_MAX;
+ bbr->ack_phase = 0;
+ bbr->bw_probe_wait = 0;
+ bbr->bw_probe_samples = 0;
+ bbr->bw_probe_up_rounds = 0;
+ bbr->bw_probe_up_acks = 0;
+
+ bbr->inflight_hi = UINT64_MAX;
+ bbr->bw_hi = UINT64_MAX;
+
+ bbr->probe_rtt_expired = 0;
+ bbr->probe_rtt_min_delay = UINT64_MAX;
+ bbr->probe_rtt_min_stamp = initial_ts;
+
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+
+ bbr->max_inflight = 0;
+
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->congestion_recovery_next_round_delivered = 0;
+
+ bbr->prior_inflight_lo = 0;
+ bbr->prior_inflight_hi = 0;
+ bbr->prior_bw_lo = 0;
+}
+
+static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr) {
+ bbr->loss_in_round = 0;
+ bbr->bw_latest = 0;
+ bbr->inflight_latest = 0;
+}
+
+static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr) {
+ bbr->bw_lo = UINT64_MAX;
+ bbr->inflight_lo = UINT64_MAX;
+}
+
+static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr) {
+ bbr->next_round_delivered = 0;
+ bbr->round_start = 0;
+ bbr->round_count = 0;
+}
+
+static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr) {
+ bbr->filled_pipe = 0;
+ bbr->full_bw = 0;
+ bbr->full_bw_count = 0;
+}
+
+static void bbr_check_startup_full_bandwidth(ngtcp2_bbr2_cc *bbr) {
+ if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) {
+ return;
+ }
+
+ if (bbr->max_bw * 100 >= bbr->full_bw * 125) {
+ bbr->full_bw = bbr->max_bw;
+ bbr->full_bw_count = 0;
+ }
+
+ ++bbr->full_bw_count;
+
+ if (bbr->full_bw_count >= 3) {
+ bbr->filled_pipe = 1;
+
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 filled pipe, full_bw=%" PRIu64, bbr->full_bw);
+ }
+}
+
+static void bbr_check_startup_high_loss(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_cc_ack *ack) {
+ if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) {
+ return;
+ }
+
+ if (bbr->loss_events_in_round <= 3) {
+ return;
+ }
+
+ /* loss_thresh = 2% */
+ if (bbr->bytes_lost_in_round * 100 <= ack->prior_bytes_in_flight * 2) {
+ return;
+ }
+
+ bbr->filled_pipe = 1;
+}
+
+static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ double nominal_bandwidth = (double)bbr->initial_cwnd;
+
+ cstat->pacing_rate = NGTCP2_BBR_STARTUP_PACING_GAIN * nominal_bandwidth /
+ (double)NGTCP2_MILLISECONDS;
+}
+
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain) {
+ double rate = pacing_gain * (double)bbr->bw *
+ (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT) / 100 / NGTCP2_SECONDS;
+
+ if (bbr->filled_pipe || rate > cstat->pacing_rate) {
+ cstat->pacing_rate = rate;
+ }
+}
+
+static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ bbr_set_pacing_rate_with_gain(bbr, cstat, bbr->pacing_gain);
+}
+
+static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Startup");
+
+ bbr->state = NGTCP2_BBR2_STATE_STARTUP;
+ bbr->pacing_gain = NGTCP2_BBR_STARTUP_PACING_GAIN;
+ bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN;
+}
+
+static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_cc_ack *ack) {
+ bbr_check_startup_full_bandwidth(bbr);
+ bbr_check_startup_high_loss(bbr, ack);
+
+ if (bbr->state == NGTCP2_BBR2_STATE_STARTUP && bbr->filled_pipe) {
+ bbr_enter_drain(bbr);
+ }
+}
+
+static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ bbr_handle_restart_from_idle(bbr, cstat, ts);
+}
+
+static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ bbr_update_model_and_state(bbr, cstat, ack, ts);
+ bbr_update_control_parameters(bbr, cstat, ack);
+}
+
+static void bbr_update_model_and_state(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ bbr_update_latest_delivery_signals(bbr, cstat);
+ bbr_update_congestion_signals(bbr, cstat, ack);
+ bbr_update_ack_aggregation(bbr, cstat, ack, ts);
+ bbr_check_startup_done(bbr, ack);
+ bbr_check_drain(bbr, cstat, ts);
+ bbr_update_probe_bw_cycle_phase(bbr, cstat, ack, ts);
+ bbr_update_min_rtt(bbr, ack, ts);
+ bbr_check_probe_rtt(bbr, cstat, ts);
+ bbr_advance_latest_delivery_signals(bbr, cstat);
+ bbr_bound_bw_for_model(bbr);
+}
+
+static void bbr_update_control_parameters(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_set_pacing_rate(bbr, cstat);
+ bbr_set_send_quantum(bbr, cstat);
+ bbr_set_cwnd(bbr, cstat, ack);
+}
+
+static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ bbr_handle_lost_packet(cc, cstat, pkt, ts);
+}
+
+static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ bbr->loss_round_start = 0;
+ bbr->bw_latest = ngtcp2_max(bbr->bw_latest, cstat->delivery_rate_sec);
+ bbr->inflight_latest =
+ ngtcp2_max(bbr->inflight_latest, bbr->rst->rs.delivered);
+
+ if (bbr->rst->rs.prior_delivered >= bbr->loss_round_delivered) {
+ bbr->loss_round_delivered = bbr->rst->delivered;
+ bbr->loss_round_start = 1;
+ }
+}
+
+static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ if (bbr->loss_round_start) {
+ bbr->bw_latest = cstat->delivery_rate_sec;
+ bbr->inflight_latest = bbr->rst->rs.delivered;
+ }
+}
+
+static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_max_bw(bbr, cstat, ack);
+
+ if (ack->bytes_lost) {
+ bbr->bytes_lost_in_round += ack->bytes_lost;
+ ++bbr->loss_events_in_round;
+
+ if (!bbr->loss_in_round) {
+ bbr->loss_in_round = 1;
+ bbr->loss_round_delivered = bbr->rst->delivered;
+ }
+ }
+
+ if (!bbr->loss_round_start) {
+ return;
+ }
+
+ bbr_adapt_lower_bounds_from_congestion(bbr, cstat);
+
+ bbr->loss_in_round = 0;
+}
+
+static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ if (!bbr->filled_pipe || bbr_is_in_probe_bw_state(bbr)) {
+ return;
+ }
+
+ if (bbr->loss_in_round) {
+ bbr_init_lower_bounds(bbr, cstat);
+ bbr_loss_lower_bounds(bbr);
+ }
+}
+
+static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ if (bbr->bw_lo == UINT64_MAX) {
+ bbr->bw_lo = bbr->max_bw;
+ }
+
+ if (bbr->inflight_lo == UINT64_MAX) {
+ bbr->inflight_lo = cstat->cwnd;
+ }
+}
+
+static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr) {
+ bbr->bw_lo = ngtcp2_max(bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER /
+ NGTCP2_BBR_BETA_DENOM);
+ bbr->inflight_lo = ngtcp2_max(bbr->inflight_latest,
+ bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER /
+ NGTCP2_BBR_BETA_DENOM);
+}
+
+static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr) {
+ bbr->bw = ngtcp2_min(bbr->max_bw, bbr->bw_lo);
+ bbr->bw = ngtcp2_min(bbr->bw, bbr->bw_hi);
+}
+
+static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_round(bbr, ack);
+ bbr_handle_recovery(bbr, cstat, ack);
+
+ if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) {
+ ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec,
+ bbr->cycle_count);
+
+ bbr->max_bw = ngtcp2_window_filter_get_best(&bbr->max_bw_filter);
+ }
+}
+
+static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack) {
+ if (ack->pkt_delivered >= bbr->next_round_delivered) {
+ bbr_start_round(bbr);
+
+ ++bbr->round_count;
+ ++bbr->rounds_since_bw_probe;
+ bbr->round_start = 1;
+
+ bbr->bytes_lost_in_round = 0;
+ bbr->loss_events_in_round = 0;
+
+ bbr->rst->is_cwnd_limited = 0;
+
+ return;
+ }
+
+ bbr->round_start = 0;
+}
+
+static void bbr_start_round(ngtcp2_bbr2_cc *bbr) {
+ bbr->next_round_delivered = bbr->rst->delivered;
+}
+
+static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr) {
+ switch (bbr->state) {
+ case NGTCP2_BBR2_STATE_PROBE_BW_DOWN:
+ case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE:
+ case NGTCP2_BBR2_STATE_PROBE_BW_REFILL:
+ case NGTCP2_BBR2_STATE_PROBE_BW_UP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ ngtcp2_duration interval = ts - bbr->extra_acked_interval_start;
+ uint64_t expected_delivered = bbr->bw * interval / NGTCP2_SECONDS;
+ uint64_t extra;
+
+ if (bbr->extra_acked_delivered <= expected_delivered) {
+ bbr->extra_acked_delivered = 0;
+ bbr->extra_acked_interval_start = ts;
+ expected_delivered = 0;
+ }
+
+ bbr->extra_acked_delivered += ack->bytes_delivered;
+ extra = bbr->extra_acked_delivered - expected_delivered;
+ extra = ngtcp2_min(extra, cstat->cwnd);
+
+ ngtcp2_window_filter_update(&bbr->extra_acked_filter, extra,
+ bbr->round_count);
+
+ bbr->extra_acked = ngtcp2_window_filter_get_best(&bbr->extra_acked_filter);
+}
+
+static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Drain");
+
+ bbr->state = NGTCP2_BBR2_STATE_DRAIN;
+ bbr->pacing_gain = 1. / NGTCP2_BBR_STARTUP_CWND_GAIN;
+ bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN;
+}
+
+static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr->state == NGTCP2_BBR2_STATE_DRAIN &&
+ cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->bw, 1.0)) {
+ bbr_enter_probe_bw(bbr, ts);
+ }
+}
+
+static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) {
+ bbr_start_probe_bw_down(bbr, ts);
+}
+
+static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 start ProbeBW_DOWN");
+
+ bbr_reset_congestion_signals(bbr);
+
+ bbr->probe_up_cnt = UINT64_MAX;
+
+ bbr_pick_probe_wait(bbr);
+
+ bbr->cycle_stamp = ts;
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING;
+
+ bbr_start_round(bbr);
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_DOWN;
+ bbr->pacing_gain = 0.9;
+ bbr->cwnd_gain = 2;
+}
+
+static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 start ProbeBW_CRUISE");
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_CRUISE;
+ bbr->pacing_gain = 1.0;
+ bbr->cwnd_gain = 2;
+}
+
+static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 start ProbeBW_REFILL");
+
+ bbr_reset_lower_bounds(bbr);
+
+ bbr->bw_probe_up_rounds = 0;
+ bbr->bw_probe_up_acks = 0;
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING;
+
+ bbr_start_round(bbr);
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_REFILL;
+ bbr->pacing_gain = 1.0;
+ bbr->cwnd_gain = 2;
+}
+
+static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 start ProbeBW_UP");
+
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING;
+
+ bbr_start_round(bbr);
+
+ bbr->cycle_stamp = ts;
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_UP;
+ bbr->pacing_gain = 1.25;
+ bbr->cwnd_gain = 2;
+
+ bbr_raise_inflight_hi_slope(bbr, cstat);
+}
+
+static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ if (!bbr->filled_pipe) {
+ return;
+ }
+
+ bbr_adapt_upper_bounds(bbr, cstat, ack, ts);
+
+ if (!bbr_is_in_probe_bw_state(bbr)) {
+ return;
+ }
+
+ switch (bbr->state) {
+ case NGTCP2_BBR2_STATE_PROBE_BW_DOWN:
+ if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) {
+ return;
+ }
+
+ if (bbr_check_time_to_cruise(bbr, cstat, ts)) {
+ bbr_start_probe_bw_cruise(bbr);
+ }
+
+ break;
+ case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE:
+ if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) {
+ return;
+ }
+
+ break;
+ case NGTCP2_BBR2_STATE_PROBE_BW_REFILL:
+ if (bbr->round_start) {
+ bbr->bw_probe_samples = 1;
+ bbr_start_probe_bw_up(bbr, cstat, ts);
+ }
+
+ break;
+ case NGTCP2_BBR2_STATE_PROBE_BW_UP:
+ if (bbr_has_elapsed_in_phase(bbr, bbr->min_rtt, ts) &&
+ cstat->bytes_in_flight > bbr_inflight(bbr, cstat, bbr->max_bw, 1.25)) {
+ bbr_start_probe_bw_down(bbr, ts);
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
+ (void)ts;
+
+ if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) {
+ return 0;
+ }
+
+ if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->max_bw, 1.0)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_duration interval,
+ ngtcp2_tstamp ts) {
+ return ts > bbr->cycle_stamp + interval;
+}
+
+static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t headroom;
+ uint64_t mpcwnd;
+ if (bbr->inflight_hi == UINT64_MAX) {
+ return UINT64_MAX;
+ }
+
+ headroom = ngtcp2_max(cstat->max_udp_payload_size,
+ bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER /
+ NGTCP2_BBR_HEADROOM_DENOM);
+ mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size);
+
+ if (bbr->inflight_hi > headroom) {
+ return ngtcp2_max(bbr->inflight_hi - headroom, mpcwnd);
+ }
+
+ return mpcwnd;
+}
+
+static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t growth_this_round = cstat->max_udp_payload_size
+ << bbr->bw_probe_up_rounds;
+
+ bbr->bw_probe_up_rounds = ngtcp2_min(bbr->bw_probe_up_rounds + 1, 30);
+ bbr->probe_up_cnt = ngtcp2_max(cstat->cwnd / growth_this_round, 1) *
+ cstat->max_udp_payload_size;
+}
+
+static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ uint64_t delta;
+
+ if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) {
+ return;
+ }
+
+ bbr->bw_probe_up_acks += ack->bytes_delivered;
+
+ if (bbr->bw_probe_up_acks >= bbr->probe_up_cnt) {
+ delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt;
+ bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt;
+ bbr->inflight_hi += delta * cstat->max_udp_payload_size;
+ }
+
+ if (bbr->round_start) {
+ bbr_raise_inflight_hi_slope(bbr, cstat);
+ }
+}
+
+static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING &&
+ bbr->round_start) {
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK;
+ }
+
+ if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING &&
+ bbr->round_start) {
+ if (bbr_is_in_probe_bw_state(bbr) && !bbr->rst->rs.is_app_limited) {
+ bbr_advance_max_bw_filter(bbr);
+ }
+ }
+
+ if (!bbr_check_inflight_too_high(bbr, cstat, ts)) {
+ /* bbr->bw_hi never be updated */
+ if (bbr->inflight_hi == UINT64_MAX /* || bbr->bw_hi == UINT64_MAX */) {
+ return;
+ }
+
+ if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) {
+ bbr->inflight_hi = bbr->rst->rs.tx_in_flight;
+ }
+
+ if (cstat->delivery_rate_sec > bbr->bw_hi) {
+ bbr->bw_hi = cstat->delivery_rate_sec;
+ }
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) {
+ bbr_probe_inflight_hi_upward(bbr, cstat, ack);
+ }
+ }
+}
+
+static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr_has_elapsed_in_phase(bbr, bbr->bw_probe_wait, ts) ||
+ bbr_is_reno_coexistence_probe_time(bbr, cstat)) {
+ bbr_start_probe_bw_refill(bbr);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr) {
+ uint8_t rand;
+
+ bbr->rand(&rand, 1, &bbr->rand_ctx);
+
+ bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256);
+
+ bbr->rand(&rand, 1, &bbr->rand_ctx);
+
+ bbr->bw_probe_wait = 2 * NGTCP2_SECONDS +
+ (ngtcp2_tstamp)((double)rand / 255. * NGTCP2_SECONDS);
+}
+
+static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t reno_rounds =
+ bbr_target_inflight(bbr, cstat) / cstat->max_udp_payload_size;
+
+ return bbr->rounds_since_bw_probe >= ngtcp2_min(reno_rounds, 63);
+}
+
+static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t bdp = bbr_inflight(bbr, cstat, bbr->bw, 1.0);
+
+ return ngtcp2_min(bdp, cstat->cwnd);
+}
+
+static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (is_inflight_too_high(&bbr->rst->rs)) {
+ if (bbr->bw_probe_samples) {
+ bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int is_inflight_too_high(const ngtcp2_rs *rs) {
+ return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM >
+ rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER;
+}
+
+static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_rs *rs,
+ ngtcp2_tstamp ts) {
+ bbr->bw_probe_samples = 0;
+
+ if (!rs->is_app_limited) {
+ bbr->prior_inflight_hi = bbr->inflight_hi;
+
+ bbr->inflight_hi = ngtcp2_max(
+ rs->tx_in_flight, bbr_target_inflight(bbr, cstat) *
+ NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM);
+ }
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) {
+ bbr_start_probe_bw_down(bbr, ts);
+ }
+}
+
+static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ ngtcp2_rs rs = {0};
+
+ if (!bbr->bw_probe_samples) {
+ return;
+ }
+
+ rs.tx_in_flight = pkt->tx_in_flight;
+ rs.lost = bbr->rst->lost - pkt->lost;
+ rs.is_app_limited = pkt->is_app_limited;
+
+ if (is_inflight_too_high(&rs)) {
+ rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt);
+
+ bbr_handle_inflight_too_high(bbr, cstat, &rs, ts);
+ }
+}
+
+static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_rs *rs,
+ const ngtcp2_cc_pkt *pkt) {
+ uint64_t inflight_prev, lost_prefix;
+ (void)bbr;
+
+ assert(rs->tx_in_flight >= pkt->pktlen);
+
+ inflight_prev = rs->tx_in_flight - pkt->pktlen;
+
+ assert(rs->lost >= pkt->pktlen);
+
+ /* bbr->rst->lost is not incremented for pkt yet */
+
+ if (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER <
+ rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM) {
+ return inflight_prev;
+ }
+
+ lost_prefix = (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER -
+ rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM) /
+ (NGTCP2_BBR_LOSS_THRESH_DENOM - NGTCP2_BBR_LOSS_THRESH_NUMER);
+
+ return inflight_prev + lost_prefix;
+}
+
+static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ int min_rtt_expired;
+
+ bbr->probe_rtt_expired =
+ ts > bbr->probe_rtt_min_stamp + NGTCP2_BBR_PROBE_RTT_INTERVAL;
+
+ if (ack->rtt != UINT64_MAX &&
+ (ack->rtt < bbr->probe_rtt_min_delay || bbr->probe_rtt_expired)) {
+ bbr->probe_rtt_min_delay = ack->rtt;
+ bbr->probe_rtt_min_stamp = ts;
+ }
+
+ min_rtt_expired = ts > bbr->min_rtt_stamp + NGTCP2_BBR_MIN_RTT_FILTERLEN;
+
+ if (bbr->probe_rtt_min_delay < bbr->min_rtt || min_rtt_expired) {
+ bbr->min_rtt = bbr->probe_rtt_min_delay;
+ bbr->min_rtt_stamp = bbr->probe_rtt_min_stamp;
+
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 update min_rtt=%" PRIu64, bbr->min_rtt);
+ }
+}
+
+static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT && bbr->probe_rtt_expired &&
+ !bbr->idle_restart) {
+ bbr_enter_probe_rtt(bbr);
+ bbr_save_cwnd(bbr, cstat);
+
+ bbr->probe_rtt_done_stamp = UINT64_MAX;
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING;
+
+ bbr_start_round(bbr);
+ }
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) {
+ bbr_handle_probe_rtt(bbr, cstat, ts);
+ }
+
+ if (bbr->rst->rs.delivered) {
+ bbr->idle_restart = 0;
+ }
+}
+
+static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter ProbeRTT");
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_RTT;
+ bbr->pacing_gain = 1;
+ bbr->cwnd_gain = NGTCP2_BBR_PROBE_RTT_CWND_GAIN;
+}
+
+static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ bbr_mark_connection_app_limited(bbr, cstat);
+
+ if (bbr->probe_rtt_done_stamp == UINT64_MAX &&
+ cstat->bytes_in_flight <= bbr_probe_rtt_cwnd(bbr, cstat)) {
+ bbr->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION;
+ bbr->probe_rtt_round_done = 0;
+
+ bbr_start_round(bbr);
+
+ return;
+ }
+
+ if (bbr->probe_rtt_done_stamp != UINT64_MAX) {
+ if (bbr->round_start) {
+ bbr->probe_rtt_round_done = 1;
+ }
+
+ if (bbr->probe_rtt_round_done) {
+ bbr_check_probe_rtt_done(bbr, cstat, ts);
+ }
+ }
+}
+
+static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr->probe_rtt_done_stamp != UINT64_MAX &&
+ ts > bbr->probe_rtt_done_stamp) {
+ bbr->probe_rtt_min_stamp = ts;
+ bbr_restore_cwnd(bbr, cstat);
+ bbr_exit_probe_rtt(bbr, ts);
+ }
+}
+
+static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t app_limited = bbr->rst->delivered + cstat->bytes_in_flight;
+
+ if (app_limited) {
+ bbr->rst->app_limited = app_limited;
+ } else {
+ bbr->rst->app_limited = cstat->max_udp_payload_size;
+ }
+}
+
+static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) {
+ bbr_reset_lower_bounds(bbr);
+
+ if (bbr->filled_pipe) {
+ bbr_start_probe_bw_down(bbr, ts);
+ bbr_start_probe_bw_cruise(bbr);
+ } else {
+ bbr_enter_startup(bbr);
+ }
+}
+
+static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (cstat->bytes_in_flight == 0 && bbr->rst->app_limited) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 restart from idle");
+
+ bbr->idle_restart = 1;
+ bbr->extra_acked_interval_start = ts;
+
+ if (bbr_is_in_probe_bw_state(bbr)) {
+ bbr_set_pacing_rate_with_gain(bbr, cstat, 1);
+ } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) {
+ bbr_check_probe_rtt_done(bbr, cstat, ts);
+ }
+ }
+}
+
+static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw,
+ double gain) {
+ uint64_t bdp;
+
+ if (bbr->min_rtt == UINT64_MAX) {
+ return bbr->initial_cwnd;
+ }
+
+ bdp = bw * bbr->min_rtt / NGTCP2_SECONDS;
+
+ return (uint64_t)(gain * (double)bdp);
+}
+
+static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) {
+ return max_udp_payload_size * 4;
+}
+
+static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ uint64_t inflight) {
+ bbr_update_offload_budget(bbr, cstat);
+
+ inflight = ngtcp2_max(inflight, bbr->offload_budget);
+ inflight = ngtcp2_max(inflight, min_pipe_cwnd(cstat->max_udp_payload_size));
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) {
+ inflight += 2 * cstat->max_udp_payload_size;
+ }
+
+ return inflight;
+}
+
+static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ uint64_t bw, double gain) {
+ uint64_t inflight = bbr_bdp_multiple(bbr, bw, gain);
+
+ return bbr_quantization_budget(bbr, cstat, inflight);
+}
+
+static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t inflight;
+
+ /* Not documented */
+ /* bbr_update_aggregation_budget(bbr); */
+
+ inflight = bbr_bdp_multiple(bbr, bbr->bw, bbr->cwnd_gain) + bbr->extra_acked;
+ bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight);
+}
+
+static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ bbr->offload_budget = 3 * cstat->send_quantum;
+}
+
+static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr) {
+ ++bbr->cycle_count;
+}
+
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (ack->bytes_lost > 0) {
+ if (cstat->cwnd > ack->bytes_lost) {
+ cstat->cwnd -= ack->bytes_lost;
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size);
+ } else {
+ cstat->cwnd = cstat->max_udp_payload_size;
+ }
+ }
+
+ if (bbr->packet_conservation) {
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered);
+ }
+}
+
+static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ if (!bbr->in_loss_recovery && bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT) {
+ bbr->prior_cwnd = cstat->cwnd;
+ return;
+ }
+
+ bbr->prior_cwnd = ngtcp2_max(bbr->prior_cwnd, cstat->cwnd);
+}
+
+static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, bbr->prior_cwnd);
+}
+
+static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t probe_rtt_cwnd =
+ bbr_bdp_multiple(bbr, bbr->bw, NGTCP2_BBR_PROBE_RTT_CWND_GAIN);
+ uint64_t mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size);
+
+ return ngtcp2_max(probe_rtt_cwnd, mpcwnd);
+}
+
+static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t probe_rtt_cwnd;
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) {
+ probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat);
+
+ cstat->cwnd = ngtcp2_min(cstat->cwnd, probe_rtt_cwnd);
+ }
+}
+
+static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ uint64_t mpcwnd;
+
+ bbr_update_max_inflight(bbr, cstat);
+ bbr_modulate_cwnd_for_recovery(bbr, cstat, ack);
+
+ if (!bbr->packet_conservation) {
+ if (bbr->filled_pipe) {
+ cstat->cwnd += ack->bytes_delivered;
+ cstat->cwnd = ngtcp2_min(cstat->cwnd, bbr->max_inflight);
+ } else if (cstat->cwnd < bbr->max_inflight ||
+ bbr->rst->delivered < bbr->initial_cwnd) {
+ cstat->cwnd += ack->bytes_delivered;
+ }
+
+ mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size);
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, mpcwnd);
+ }
+
+ bbr_bound_cwnd_for_probe_rtt(bbr, cstat);
+ bbr_bound_cwnd_for_model(bbr, cstat);
+}
+
+static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t cap = UINT64_MAX;
+ uint64_t mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size);
+
+ if (bbr_is_in_probe_bw_state(bbr) &&
+ bbr->state != NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) {
+ cap = bbr->inflight_hi;
+ } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT ||
+ bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) {
+ cap = bbr_inflight_with_headroom(bbr, cstat);
+ }
+
+ cap = ngtcp2_min(cap, bbr->inflight_lo);
+ cap = ngtcp2_max(cap, mpcwnd);
+
+ cstat->cwnd = ngtcp2_min(cstat->cwnd, cap);
+}
+
+static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ size_t send_quantum =
+ (size_t)(cstat->pacing_rate * (double)(bbr->min_rtt == UINT64_MAX
+ ? NGTCP2_MILLISECONDS
+ : bbr->min_rtt));
+ (void)bbr;
+
+ cstat->send_quantum = ngtcp2_min(send_quantum, 64 * 1024);
+ cstat->send_quantum =
+ ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10);
+}
+
+static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_time) {
+ return cstat->congestion_recovery_start_ts != UINT64_MAX &&
+ sent_time <= cstat->congestion_recovery_start_ts;
+}
+
+static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (bbr->in_loss_recovery) {
+ if (ack->pkt_delivered >= bbr->congestion_recovery_next_round_delivered) {
+ bbr->packet_conservation = 0;
+ }
+
+ if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) {
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+ bbr_restore_cwnd(bbr, cstat);
+ }
+
+ return;
+ }
+
+ if (bbr->congestion_recovery_start_ts != UINT64_MAX) {
+ bbr->in_loss_recovery = 1;
+ bbr_save_cwnd(bbr, cstat);
+ cstat->cwnd = cstat->bytes_in_flight +
+ ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size);
+
+ cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts;
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->packet_conservation = 1;
+ bbr->congestion_recovery_next_round_delivered = bbr->rst->delivered;
+ bbr->prior_inflight_lo = bbr->inflight_lo;
+ bbr->prior_bw_lo = bbr->bw_lo;
+ }
+}
+
+static void bbr2_cc_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
+ ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
+ ngtcp2_log *log) {
+ bbr->ccb.log = log;
+ bbr->rst = rst;
+ bbr->rand = rand;
+ bbr->rand_ctx = *rand_ctx;
+ bbr->initial_cwnd = cstat->cwnd;
+
+ bbr_on_init(bbr, cstat, initial_ts);
+}
+
+static void bbr2_cc_free(ngtcp2_bbr2_cc *bbr) { (void)bbr; }
+
+static void bbr2_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)pkt;
+ (void)ts;
+}
+
+static void bbr2_cc_on_pkt_lost(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_update_on_loss(bbr, cstat, pkt, ts);
+}
+
+static void bbr2_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ if (!bbr->filled_pipe || bbr->in_loss_recovery ||
+ bbr->congestion_recovery_start_ts != UINT64_MAX ||
+ in_congestion_recovery(cstat, sent_ts)) {
+ return;
+ }
+
+ bbr->congestion_recovery_start_ts = ts;
+}
+
+static void bbr2_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+ (void)ts;
+
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ if (bbr->in_loss_recovery) {
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+ bbr_restore_cwnd(bbr, cstat);
+ bbr->full_bw_count = 0;
+ bbr->loss_in_round = 0;
+ bbr->inflight_lo = ngtcp2_max(bbr->inflight_lo, bbr->prior_inflight_lo);
+ bbr->inflight_hi = ngtcp2_max(bbr->inflight_hi, bbr->prior_inflight_hi);
+ bbr->bw_lo = ngtcp2_max(bbr->bw_lo, bbr->prior_bw_lo);
+ }
+}
+
+static void bbr2_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+ (void)ts;
+
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+
+ bbr_save_cwnd(bbr, cstat);
+ cstat->cwnd = cstat->bytes_in_flight + cstat->max_udp_payload_size;
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size));
+}
+
+static void bbr2_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_update_on_ack(bbr, cstat, ack, ts);
+}
+
+static void bbr2_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_on_transmit(bbr, cstat, pkt->sent_ts);
+}
+
+static void bbr2_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)ts;
+}
+
+static void bbr2_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_on_init(bbr, cstat, ts);
+}
+
+static void bbr2_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)event;
+ (void)ts;
+}
+
+int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem) {
+ ngtcp2_bbr2_cc *bbr;
+
+ bbr = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr2_cc));
+ if (bbr == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ bbr2_cc_init(bbr, cstat, rst, initial_ts, rand, rand_ctx, log);
+
+ cc->ccb = &bbr->ccb;
+ cc->on_pkt_acked = bbr2_cc_on_pkt_acked;
+ cc->on_pkt_lost = bbr2_cc_on_pkt_lost;
+ cc->congestion_event = bbr2_cc_congestion_event;
+ cc->on_spurious_congestion = bbr2_cc_on_spurious_congestion;
+ cc->on_persistent_congestion = bbr2_cc_on_persistent_congestion;
+ cc->on_ack_recv = bbr2_cc_on_ack_recv;
+ cc->on_pkt_sent = bbr2_cc_on_pkt_sent;
+ cc->new_rtt_sample = bbr2_cc_new_rtt_sample;
+ cc->reset = bbr2_cc_reset;
+ cc->event = bbr2_cc_event;
+
+ return 0;
+}
+
+void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr2_cc_free(bbr);
+ ngtcp2_mem_free(mem, bbr);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h
new file mode 100644
index 00000000000..50dc05a5f26
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h
@@ -0,0 +1,149 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BBR2_H
+#define NGTCP2_BBR2_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_cc.h"
+#include "ngtcp2_window_filter.h"
+
+typedef struct ngtcp2_rst ngtcp2_rst;
+
+typedef enum ngtcp2_bbr2_state {
+ NGTCP2_BBR2_STATE_STARTUP,
+ NGTCP2_BBR2_STATE_DRAIN,
+ NGTCP2_BBR2_STATE_PROBE_BW_DOWN,
+ NGTCP2_BBR2_STATE_PROBE_BW_CRUISE,
+ NGTCP2_BBR2_STATE_PROBE_BW_REFILL,
+ NGTCP2_BBR2_STATE_PROBE_BW_UP,
+ NGTCP2_BBR2_STATE_PROBE_RTT,
+} ngtcp2_bbr2_state;
+
+typedef enum ngtcp2_bbr2_ack_phase {
+ NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING,
+ NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING,
+ NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK,
+ NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING,
+} ngtcp2_bbr2_ack_phase;
+
+/*
+ * ngtcp2_bbr2_cc is BBR v2 congestion controller, described in
+ * https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control-01
+ */
+typedef struct ngtcp2_bbr2_cc {
+ ngtcp2_cc_base ccb;
+
+ uint64_t initial_cwnd;
+ ngtcp2_rst *rst;
+ ngtcp2_rand rand;
+ ngtcp2_rand_ctx rand_ctx;
+
+ /* max_bw_filter for tracking the maximum recent delivery rate
+ samples for estimating max_bw. */
+ ngtcp2_window_filter max_bw_filter;
+
+ ngtcp2_window_filter extra_acked_filter;
+
+ ngtcp2_duration min_rtt;
+ ngtcp2_tstamp min_rtt_stamp;
+ ngtcp2_tstamp probe_rtt_done_stamp;
+ int probe_rtt_round_done;
+ uint64_t prior_cwnd;
+ int idle_restart;
+ ngtcp2_tstamp extra_acked_interval_start;
+ uint64_t extra_acked_delivered;
+
+ /* Congestion signals */
+ int loss_in_round;
+ uint64_t bw_latest;
+ uint64_t inflight_latest;
+
+ /* Lower bounds */
+ uint64_t bw_lo;
+ uint64_t inflight_lo;
+
+ /* Round counting */
+ uint64_t next_round_delivered;
+ int round_start;
+ uint64_t round_count;
+
+ /* Full pipe */
+ int filled_pipe;
+ uint64_t full_bw;
+ size_t full_bw_count;
+
+ /* Pacing rate */
+ double pacing_gain;
+
+ ngtcp2_bbr2_state state;
+ double cwnd_gain;
+
+ int loss_round_start;
+ uint64_t loss_round_delivered;
+ uint64_t rounds_since_bw_probe;
+ uint64_t max_bw;
+ uint64_t bw;
+ uint64_t cycle_count;
+ uint64_t extra_acked;
+ uint64_t bytes_lost_in_round;
+ size_t loss_events_in_round;
+ uint64_t offload_budget;
+ uint64_t probe_up_cnt;
+ ngtcp2_tstamp cycle_stamp;
+ ngtcp2_bbr2_ack_phase ack_phase;
+ ngtcp2_duration bw_probe_wait;
+ int bw_probe_samples;
+ size_t bw_probe_up_rounds;
+ uint64_t bw_probe_up_acks;
+ uint64_t inflight_hi;
+ uint64_t bw_hi;
+ int probe_rtt_expired;
+ ngtcp2_duration probe_rtt_min_delay;
+ ngtcp2_tstamp probe_rtt_min_stamp;
+ int in_loss_recovery;
+ int packet_conservation;
+ uint64_t max_inflight;
+ ngtcp2_tstamp congestion_recovery_start_ts;
+ uint64_t congestion_recovery_next_round_delivered;
+
+ uint64_t prior_inflight_lo;
+ uint64_t prior_inflight_hi;
+ uint64_t prior_bw_lo;
+} ngtcp2_bbr2_cc;
+
+int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+#endif /* NGTCP2_BBR2_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c
index 373f23d91ae..75326d6b76b 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c
@@ -23,6 +23,7 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ngtcp2_buf.h"
+#include "ngtcp2_mem.h"
void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
buf->begin = buf->pos = buf->last = begin;
@@ -31,14 +32,25 @@ void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; }
-size_t ngtcp2_buf_left(const ngtcp2_buf *buf) {
- return (size_t)(buf->end - buf->last);
+size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) {
+ return (size_t)(buf->end - buf->begin);
}
-size_t ngtcp2_buf_len(const ngtcp2_buf *buf) {
- return (size_t)(buf->last - buf->pos);
+int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len,
+ const ngtcp2_mem *mem) {
+ *pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len);
+ if (*pbufchain == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ (*pbufchain)->next = NULL;
+
+ ngtcp2_buf_init(&(*pbufchain)->buf,
+ (uint8_t *)(*pbufchain) + sizeof(ngtcp2_buf_chain), len);
+
+ return 0;
}
-size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) {
- return (size_t)(buf->end - buf->begin);
+void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, bufchain);
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h
index 8f2c36a1bbb..107d413382d 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h
@@ -62,13 +62,13 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf);
* written to the underlying buffer. In other words, it returns
* buf->end - buf->last.
*/
-size_t ngtcp2_buf_left(const ngtcp2_buf *buf);
+#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last)
/*
* ngtcp2_buf_len returns the number of bytes left to read. In other
* words, it returns buf->last - buf->pos.
*/
-size_t ngtcp2_buf_len(const ngtcp2_buf *buf);
+#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos)
/*
* ngtcp2_buf_cap returns the capacity of the buffer. In other words,
@@ -76,4 +76,33 @@ size_t ngtcp2_buf_len(const ngtcp2_buf *buf);
*/
size_t ngtcp2_buf_cap(const ngtcp2_buf *buf);
+/*
+ * ngtcp2_buf_chain is a linked list of ngtcp2_buf.
+ */
+typedef struct ngtcp2_buf_chain ngtcp2_buf_chain;
+
+struct ngtcp2_buf_chain {
+ ngtcp2_buf_chain *next;
+ ngtcp2_buf buf;
+};
+
+/*
+ * ngtcp2_buf_chain_new creates new ngtcp2_buf_chain and initializes
+ * the internal buffer with |len| bytes space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_buf_chain_del deletes the resource allocated by |bufchain|.
+ * It also deletes the memory pointed by |bufchain|.
+ */
+void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem);
+
#endif /* NGTCP2_BUF_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c
index f4670805c73..1ee7d96b047 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c
@@ -43,11 +43,15 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
size_t pktlen, ngtcp2_pktns_id pktns_id,
- ngtcp2_tstamp ts_sent) {
+ ngtcp2_tstamp sent_ts, uint64_t lost,
+ uint64_t tx_in_flight, int is_app_limited) {
pkt->pkt_num = pkt_num;
pkt->pktlen = pktlen;
pkt->pktns_id = pktns_id;
- pkt->ts_sent = ts_sent;
+ pkt->sent_ts = sent_ts;
+ pkt->lost = lost;
+ pkt->tx_in_flight = tx_in_flight;
+ pkt->is_app_limited = is_app_limited;
return pkt;
}
@@ -106,7 +110,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
uint64_t m;
(void)ts;
- if (in_congestion_recovery(cstat, pkt->ts_sent)) {
+ if (in_congestion_recovery(cstat, pkt->sent_ts)) {
return;
}
@@ -129,12 +133,12 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
}
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts_sent,
+ ngtcp2_tstamp sent_ts,
ngtcp2_tstamp ts) {
ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
uint64_t min_cwnd;
- if (in_congestion_recovery(cstat, ts_sent)) {
+ if (in_congestion_recovery(cstat, sent_ts)) {
return;
}
@@ -162,9 +166,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx,
}
void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts) {
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
uint64_t target_cwnd, initcwnd;
+ (void)ack;
(void)ts;
/* TODO Use sliding window for min rtt measurement */
@@ -184,8 +189,12 @@ void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
}
}
-void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx) {
+void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ (void)cstat;
+ (void)ts;
+
reno_cc_reset(cc);
}
@@ -198,6 +207,14 @@ static void cubic_cc_reset(ngtcp2_cubic_cc *cc) {
cc->epoch_start = UINT64_MAX;
cc->k = 0;
+ cc->prior.cwnd = 0;
+ cc->prior.ssthresh = 0;
+ cc->prior.w_last_max = 0;
+ cc->prior.w_tcp = 0;
+ cc->prior.origin_point = 0;
+ cc->prior.epoch_start = UINT64_MAX;
+ cc->prior.k = 0;
+
cc->rtt_sample_count = 0;
cc->current_round_min_rtt = UINT64_MAX;
cc->last_round_min_rtt = UINT64_MAX;
@@ -225,6 +242,7 @@ int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
cc->ccb = &cubic_cc->ccb;
cc->on_pkt_acked = ngtcp2_cc_cubic_cc_on_pkt_acked;
cc->congestion_event = ngtcp2_cc_cubic_cc_congestion_event;
+ cc->on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion;
cc->on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion;
cc->on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv;
cc->on_pkt_sent = ngtcp2_cc_cubic_cc_on_pkt_sent;
@@ -300,7 +318,7 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
cc->window_end = -1;
}
- if (in_congestion_recovery(cstat, pkt->ts_sent)) {
+ if (in_congestion_recovery(cstat, pkt->sent_ts)) {
return;
}
@@ -421,15 +439,25 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx,
ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts_sent,
+ ngtcp2_tstamp sent_ts,
ngtcp2_tstamp ts) {
ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
uint64_t min_cwnd;
- if (in_congestion_recovery(cstat, ts_sent)) {
+ if (in_congestion_recovery(cstat, sent_ts)) {
return;
}
+ if (cc->prior.cwnd < cstat->cwnd) {
+ cc->prior.cwnd = cstat->cwnd;
+ cc->prior.ssthresh = cstat->ssthresh;
+ cc->prior.w_last_max = cc->w_last_max;
+ cc->prior.w_tcp = cc->w_tcp;
+ cc->prior.origin_point = cc->origin_point;
+ cc->prior.epoch_start = cc->epoch_start;
+ cc->prior.k = cc->k;
+ }
+
cstat->congestion_recovery_start_ts = ts;
cc->epoch_start = UINT64_MAX;
@@ -449,6 +477,40 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx,
cstat->cwnd);
}
+void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ (void)ts;
+
+ if (cstat->cwnd >= cc->prior.cwnd) {
+ return;
+ }
+
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ cstat->cwnd = cc->prior.cwnd;
+ cstat->ssthresh = cc->prior.ssthresh;
+ cc->w_last_max = cc->prior.w_last_max;
+ cc->w_tcp = cc->prior.w_tcp;
+ cc->origin_point = cc->prior.origin_point;
+ cc->epoch_start = cc->prior.epoch_start;
+ cc->k = cc->prior.k;
+
+ cc->prior.cwnd = 0;
+ cc->prior.ssthresh = 0;
+ cc->prior.w_last_max = 0;
+ cc->prior.w_tcp = 0;
+ cc->prior.origin_point = 0;
+ cc->prior.epoch_start = UINT64_MAX;
+ cc->prior.k = 0;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "spurious congestion is detected and congestion state is "
+ "restored cwnd=%" PRIu64,
+ cstat->cwnd);
+}
+
void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx,
ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
@@ -460,9 +522,11 @@ void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx,
}
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
uint64_t target_cwnd, initcwnd;
+ (void)ack;
(void)ts;
/* TODO Use sliding window for min rtt measurement */
@@ -511,8 +575,12 @@ void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
++cc->rtt_sample_count;
}
-void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx) {
+void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ (void)cstat;
+ (void)ts;
+
cubic_cc_reset(cc);
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h
index e1ca7594802..6d9e0c2459e 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h
@@ -36,6 +36,273 @@
typedef struct ngtcp2_log ngtcp2_log;
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc_base` is the base structure of custom congestion
+ * control algorithm. It must be the first field of custom congestion
+ * controller.
+ */
+typedef struct ngtcp2_cc_base {
+ /**
+ * :member:`log` is ngtcp2 library internal logger.
+ */
+ ngtcp2_log *log;
+} ngtcp2_cc_base;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc_pkt` is a convenient structure to include
+ * acked/lost/sent packet.
+ */
+typedef struct ngtcp2_cc_pkt {
+ /**
+ * :member:`pkt_num` is the packet number
+ */
+ int64_t pkt_num;
+ /**
+ * :member:`pktlen` is the length of packet.
+ */
+ size_t pktlen;
+ /**
+ * :member:`pktns_id` is the ID of packet number space which this
+ * packet belongs to.
+ */
+ ngtcp2_pktns_id pktns_id;
+ /**
+ * :member:`sent_ts` is the timestamp when packet is sent.
+ */
+ ngtcp2_tstamp sent_ts;
+ /**
+ * :member:`lost` is the number of bytes lost when this packet was
+ * sent.
+ */
+ uint64_t lost;
+ /**
+ * :member:`tx_in_flight` is the bytes in flight when this packet
+ * was sent.
+ */
+ uint64_t tx_in_flight;
+ /**
+ * :member:`is_app_limited` is nonzero if the connection is
+ * app-limited when this packet was sent.
+ */
+ int is_app_limited;
+} ngtcp2_cc_pkt;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc_ack` is a convenient structure which stores
+ * acknowledged and lost bytes.
+ */
+typedef struct ngtcp2_cc_ack {
+ /**
+ * :member:`prior_bytes_in_flight` is the in-flight bytes before
+ * processing this ACK.
+ */
+ uint64_t prior_bytes_in_flight;
+ /**
+ * :member:`bytes_delivered` is the number of bytes acknowledged.
+ */
+ uint64_t bytes_delivered;
+ /**
+ * :member:`bytes_lost` is the number of bytes declared lost.
+ */
+ uint64_t bytes_lost;
+ /**
+ * :member:`pkt_delivered` is the cumulative acknowledged bytes when
+ * the last packet acknowledged by this ACK was sent.
+ */
+ uint64_t pkt_delivered;
+ /**
+ * :member:`largest_acked_sent_ts` is the time when the largest
+ * acknowledged packet was sent.
+ */
+ ngtcp2_tstamp largest_acked_sent_ts;
+ /**
+ * :member:`rtt` is the RTT sample. It is UINT64_MAX if no RTT
+ * sample is available.
+ */
+ ngtcp2_duration rtt;
+} ngtcp2_cc_ack;
+
+typedef struct ngtcp2_cc ngtcp2_cc;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is
+ * called with an acknowledged packet.
+ */
+typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_pkt_lost` is a callback function which is
+ * called with a lost packet.
+ */
+typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts);
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_congestion_event` is a callback function which is
+ * called when congestion event happens (e.g., when packet is lost).
+ */
+typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_spurious_congestion` is a callback function
+ * which is called when a spurious congestion is detected.
+ */
+typedef void (*ngtcp2_cc_on_spurious_congestion)(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function
+ * which is called when persistent congestion is established.
+ */
+typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is
+ * called when an acknowledgement is received.
+ */
+typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is
+ * called when an ack-eliciting packet is sent.
+ */
+typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is
+ * called when new RTT sample is obtained.
+ */
+typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_reset` is a callback function which is called when
+ * congestion state must be reset.
+ */
+typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_cc_event_type` defines congestion control events.
+ */
+typedef enum ngtcp2_cc_event_type {
+ /**
+ * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet
+ * is sent and no other ack-eliciting packet is present.
+ */
+ NGTCP2_CC_EVENT_TYPE_TX_START
+} ngtcp2_cc_event_type;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_event` is a callback function which is called when
+ * a specific event happens.
+ */
+typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc` is congestion control algorithm interface to
+ * allow custom implementation.
+ */
+typedef struct ngtcp2_cc {
+ /**
+ * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which
+ * usually contains a state.
+ */
+ ngtcp2_cc_base *ccb;
+ /**
+ * :member:`on_pkt_acked` is a callback function which is called
+ * when a packet is acknowledged.
+ */
+ ngtcp2_cc_on_pkt_acked on_pkt_acked;
+ /**
+ * :member:`on_pkt_lost` is a callback function which is called when
+ * a packet is lost.
+ */
+ ngtcp2_cc_on_pkt_lost on_pkt_lost;
+ /**
+ * :member:`congestion_event` is a callback function which is called
+ * when congestion event happens (.e.g, packet is lost).
+ */
+ ngtcp2_cc_congestion_event congestion_event;
+ /**
+ * :member:`on_spurious_congestion` is a callback function which is
+ * called when a spurious congestion is detected.
+ */
+ ngtcp2_cc_on_spurious_congestion on_spurious_congestion;
+ /**
+ * :member:`on_persistent_congestion` is a callback function which
+ * is called when persistent congestion is established.
+ */
+ ngtcp2_cc_on_persistent_congestion on_persistent_congestion;
+ /**
+ * :member:`on_ack_recv` is a callback function which is called when
+ * an acknowledgement is received.
+ */
+ ngtcp2_cc_on_ack_recv on_ack_recv;
+ /**
+ * :member:`on_pkt_sent` is a callback function which is called when
+ * ack-eliciting packet is sent.
+ */
+ ngtcp2_cc_on_pkt_sent on_pkt_sent;
+ /**
+ * :member:`new_rtt_sample` is a callback function which is called
+ * when new RTT sample is obtained.
+ */
+ ngtcp2_cc_new_rtt_sample new_rtt_sample;
+ /**
+ * :member:`reset` is a callback function which is called when
+ * congestion control state must be reset.
+ */
+ ngtcp2_cc_reset reset;
+ /**
+ * :member:`event` is a callback function which is called when a
+ * specific event happens.
+ */
+ ngtcp2_cc_event event;
+} ngtcp2_cc;
+
/*
* ngtcp2_cc_compute_initcwnd computes initial cwnd.
*/
@@ -43,7 +310,8 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size);
ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
size_t pktlen, ngtcp2_pktns_id pktns_id,
- ngtcp2_tstamp ts_sent);
+ ngtcp2_tstamp sent_ts, uint64_t lost,
+ uint64_t tx_in_flight, int is_app_limited);
/* ngtcp2_reno_cc is the RENO congestion controller. */
typedef struct ngtcp2_reno_cc {
@@ -66,7 +334,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts_sent,
+ ngtcp2_tstamp sent_ts,
ngtcp2_tstamp ts);
void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
@@ -74,9 +342,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
ngtcp2_tstamp ts);
void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
-void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc);
+void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
/* ngtcp2_cubic_cc is CUBIC congestion controller. */
typedef struct ngtcp2_cubic_cc {
@@ -88,6 +357,18 @@ typedef struct ngtcp2_cubic_cc {
uint64_t origin_point;
ngtcp2_tstamp epoch_start;
uint64_t k;
+ /* prior stores the congestion state when a congestion event occurs
+ in order to restore the state when it turns out that the event is
+ spurious. */
+ struct {
+ uint64_t cwnd;
+ uint64_t ssthresh;
+ uint64_t w_last_max;
+ uint64_t w_tcp;
+ uint64_t origin_point;
+ ngtcp2_tstamp epoch_start;
+ uint64_t k;
+ } prior;
/* HyStart++ variables */
size_t rtt_sample_count;
uint64_t current_round_min_rtt;
@@ -111,15 +392,19 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts_sent,
+ ngtcp2_tstamp sent_ts,
ngtcp2_tstamp ts);
+void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc,
ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt);
@@ -127,7 +412,8 @@ void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
-void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc);
+void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c
index cdf31f8f624..f3b92b569ec 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c
@@ -30,7 +30,7 @@
#include "ngtcp2_path.h"
#include "ngtcp2_str.h"
-void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; }
+void ngtcp2_cid_zero(ngtcp2_cid *cid) { memset(cid, 0, sizeof(*cid)); }
void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) {
assert(datalen <= NGTCP2_MAX_CIDLEN);
@@ -56,23 +56,17 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) {
int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; }
-void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid,
- const uint8_t *token) {
+void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid) {
scid->pe.index = NGTCP2_PQ_BAD_INDEX;
scid->seq = seq;
scid->cid = *cid;
- scid->ts_retired = UINT64_MAX;
+ scid->retired_ts = UINT64_MAX;
scid->flags = NGTCP2_SCID_FLAG_NONE;
- if (token) {
- memcpy(scid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
- } else {
- memset(scid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN);
- }
}
void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) {
- ngtcp2_scid_init(dest, src->seq, &src->cid, src->token);
- dest->ts_retired = src->ts_retired;
+ ngtcp2_scid_init(dest, src->seq, &src->cid);
+ dest->retired_ts = src->retired_ts;
dest->flags = src->flags;
}
@@ -82,35 +76,58 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
dcid->cid = *cid;
if (token) {
memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ dcid->flags = NGTCP2_DCID_FLAG_TOKEN_PRESENT;
} else {
- memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN);
+ dcid->flags = NGTCP2_DCID_FLAG_NONE;
}
ngtcp2_path_storage_zero(&dcid->ps);
- dcid->ts_retired = UINT64_MAX;
- dcid->flags = NGTCP2_DCID_FLAG_NONE;
+ dcid->retired_ts = UINT64_MAX;
+ dcid->bound_ts = UINT64_MAX;
dcid->bytes_sent = 0;
dcid->bytes_recv = 0;
+ dcid->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+}
+
+void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token) {
+ assert(token);
+
+ dcid->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+}
+
+void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) {
+ ngtcp2_path_copy(&dcid->ps.path, path);
}
void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
- ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token);
+ ngtcp2_dcid_init(dest, src->seq, &src->cid,
+ (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? src->token
+ : NULL);
ngtcp2_path_copy(&dest->ps.path, &src->ps.path);
- dest->ts_retired = src->ts_retired;
+ dest->retired_ts = src->retired_ts;
+ dest->bound_ts = src->bound_ts;
dest->flags = src->flags;
dest->bytes_sent = src->bytes_sent;
dest->bytes_recv = src->bytes_recv;
+ dest->max_udp_payload_size = src->max_udp_payload_size;
}
void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
dest->seq = src->seq;
dest->cid = src->cid;
- memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ if (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
+ dest->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ } else if (dest->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
+ dest->flags &= (uint8_t)~NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ }
}
int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
const ngtcp2_cid *cid, const uint8_t *token) {
if (dcid->seq == seq) {
return ngtcp2_cid_eq(&dcid->cid, cid) &&
+ (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
memcmp(dcid->token, token,
NGTCP2_STATELESS_RESET_TOKENLEN) == 0
? 0
@@ -119,3 +136,12 @@ int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO;
}
+
+int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
+ const uint8_t *token) {
+ return (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
+ ngtcp2_cmemeq(dcid->token, token,
+ NGTCP2_STATELESS_RESET_TOKENLEN)
+ ? 0
+ : NGTCP2_ERR_INVALID_ARGUMENT;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h
index a356b432d4f..0b37441178c 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h
@@ -34,14 +34,14 @@
#include "ngtcp2_pq.h"
#include "ngtcp2_path.h"
-/* NGTCP2_SCID_FLAG_NONE indicats that no flag is set. */
-#define NGTCP2_SCID_FLAG_NONE 0x00
+/* NGTCP2_SCID_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_SCID_FLAG_NONE 0x00u
/* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that
a remote endpoint uses a particular Connection ID. */
-#define NGTCP2_SCID_FLAG_USED 0x01
+#define NGTCP2_SCID_FLAG_USED 0x01u
/* NGTCP2_SCID_FLAG_RETIRED indicates that a particular Connection ID
is retired. */
-#define NGTCP2_SCID_FLAG_RETIRED 0x02
+#define NGTCP2_SCID_FLAG_RETIRED 0x02u
typedef struct ngtcp2_scid {
ngtcp2_pq_entry pe;
@@ -49,22 +49,21 @@ typedef struct ngtcp2_scid {
uint64_t seq;
/* cid is a connection ID */
ngtcp2_cid cid;
- /* ts_retired is the timestamp when peer tells that this CID is
+ /* retired_ts is the timestamp when peer tells that this CID is
retired. */
- ngtcp2_tstamp ts_retired;
+ ngtcp2_tstamp retired_ts;
/* flags is the bitwise OR of zero or more of NGTCP2_SCID_FLAG_*. */
uint8_t flags;
- /* token is a stateless reset token associated to this CID.
- Actually, the stateless reset token is tied to the connection,
- not to the particular connection ID. */
- uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
} ngtcp2_scid;
/* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_DCID_FLAG_NONE 0x00
+#define NGTCP2_DCID_FLAG_NONE 0x00u
/* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path
has been validated. */
-#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01
+#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01u
+/* NGTCP2_DCID_FLAG_TOKEN_PRESENT indicates that a stateless reset
+ token is set in token field. */
+#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02u
typedef struct ngtcp2_dcid {
/* seq is the sequence number associated to the CID. */
@@ -74,14 +73,21 @@ typedef struct ngtcp2_dcid {
/* path is a path which cid is bound to. The addresses are zero
length if cid has not been bound to a particular path yet. */
ngtcp2_path_storage ps;
- /* ts_retired is the timestamp when peer tells that this CID is
+ /* retired_ts is the timestamp when peer tells that this CID is
retired. */
- ngtcp2_tstamp ts_retired;
+ ngtcp2_tstamp retired_ts;
+ /* bound_ts is the timestamp when this connection ID is bound to a
+ particular path. It is only assigned when a connection ID is
+ used just for sending PATH_RESPONSE and is not zero-length. */
+ ngtcp2_tstamp bound_ts;
/* bytes_sent is the number of bytes sent to an associated path. */
uint64_t bytes_sent;
/* bytes_recv is the number of bytes received from an associated
path. */
uint64_t bytes_recv;
+ /* max_udp_payload_size is the maximum size of UDP payload that is
+ allowed to send to this path. */
+ size_t max_udp_payload_size;
/* flags is bitwise OR of zero or more of NGTCP2_DCID_FLAG_*. */
uint8_t flags;
/* token is a stateless reset token associated to this CID.
@@ -94,12 +100,6 @@ typedef struct ngtcp2_dcid {
void ngtcp2_cid_zero(ngtcp2_cid *cid);
/*
- * ngtcp2_cid_eq returns nonzero if |cid| and |other| share the same
- * connection ID.
- */
-int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other);
-
-/*
* ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller
* than |rhs|.
*/
@@ -112,12 +112,9 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs);
int ngtcp2_cid_empty(const ngtcp2_cid *cid);
/*
- * ngtcp2_scid_init initializes |scid| with the given parameters. If
- * |token| is NULL, the function fills scid->token it with 0. |token|
- * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
+ * ngtcp2_scid_init initializes |scid| with the given parameters.
*/
-void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid,
- const uint8_t *token);
+void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid);
/*
* ngtcp2_scid_copy copies |src| into |dest|.
@@ -133,6 +130,19 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
const uint8_t *token);
/*
+ * ngtcp2_dcid_set_token sets |token| to |dcid|. |token| must not be
+ * NULL and must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
+ */
+void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token);
+
+/*
+ * ngtcp2_dcid_set_path sets |path| to |dcid|. It sets
+ * max_udp_payload_size to the minimum UDP payload size supported
+ * by the IP protocol version.
+ */
+void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path);
+
+/*
* ngtcp2_dcid_copy copies |src| into |dest|.
*/
void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
@@ -150,4 +160,16 @@ void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
const ngtcp2_cid *cid, const uint8_t *token);
+/*
+ * ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset
+ * token |token| against the one included in |dcid|. This function
+ * returns 0 if the verification succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Tokens do not match; or |dcid| does not contain a token.
+ */
+int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
+ const uint8_t *token);
+
#endif /* NGTCP2_CID_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c
index c8b1d15db5a..a3135680ca1 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c
@@ -26,7 +26,6 @@
#include <string.h>
#include <assert.h>
-#include <math.h>
#include "ngtcp2_macro.h"
#include "ngtcp2_log.h"
@@ -43,6 +42,9 @@
/* NGTCP2_FLOW_WINDOW_SCALING_FACTOR is the growth factor of flow
control window. */
#define NGTCP2_FLOW_WINDOW_SCALING_FACTOR 2
+/* NGTCP2_MIN_COALESCED_PAYLOADLEN is the minimum length of QUIC
+ packet payload that should be coalesced to a long packet. */
+#define NGTCP2_MIN_COALESCED_PAYLOADLEN 128
/*
* conn_local_stream returns nonzero if |stream_id| indicates that it
@@ -58,6 +60,15 @@ static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) {
*/
static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; }
+/*
+ * conn_is_handshake_completed returns nonzero if QUIC handshake has
+ * completed.
+ */
+static int conn_is_handshake_completed(ngtcp2_conn *conn) {
+ return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+ conn->pktns.crypto.rx.ckm && conn->pktns.crypto.tx.ckm;
+}
+
static int conn_call_recv_client_initial(ngtcp2_conn *conn,
const ngtcp2_cid *dcid) {
int rv;
@@ -123,6 +134,8 @@ static int conn_call_recv_crypto_data(ngtcp2_conn *conn,
case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
case NGTCP2_ERR_TRANSPORT_PARAM:
case NGTCP2_ERR_PROTO:
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ case NGTCP2_ERR_NOMEM:
case NGTCP2_ERR_CALLBACK_FAILURE:
return rv;
default:
@@ -145,16 +158,21 @@ static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) {
return 0;
}
-static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm,
- uint64_t app_error_code) {
+static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm) {
int rv;
+ uint32_t flags = NGTCP2_STREAM_CLOSE_FLAG_NONE;
if (!conn->callbacks.stream_close) {
return 0;
}
- rv = conn->callbacks.stream_close(conn, strm->stream_id, app_error_code,
- conn->user_data, strm->stream_user_data);
+ if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) {
+ flags |= NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET;
+ }
+
+ rv = conn->callbacks.stream_close(conn, flags, strm->stream_id,
+ strm->app_error_code, conn->user_data,
+ strm->stream_user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -245,15 +263,21 @@ static int conn_call_remove_connection_id(ngtcp2_conn *conn,
return 0;
}
-static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path,
+static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv,
ngtcp2_path_validation_result res) {
int rv;
+ uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE;
if (!conn->callbacks.path_validation) {
return 0;
}
- rv = conn->callbacks.path_validation(conn, path, res, conn->user_data);
+ if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) {
+ flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR;
+ }
+
+ rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, res,
+ conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -262,17 +286,18 @@ static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path,
}
static int conn_call_select_preferred_addr(ngtcp2_conn *conn,
- ngtcp2_addr *dest) {
+ ngtcp2_path *dest) {
int rv;
if (!conn->callbacks.select_preferred_addr) {
return 0;
}
- assert(conn->remote.transport_params.preferred_address_present);
+ assert(conn->remote.transport_params);
+ assert(conn->remote.transport_params->preferred_address_present);
rv = conn->callbacks.select_preferred_addr(
- conn, dest, &conn->remote.transport_params.preferred_address,
+ conn, dest, &conn->remote.transport_params->preferred_address,
conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
@@ -345,8 +370,7 @@ static int conn_call_dcid_status(ngtcp2_conn *conn,
rv = conn->callbacks.dcid_status(
conn, (int)type, dcid->seq, &dcid->cid,
- ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL
- : dcid->token,
+ (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? dcid->token : NULL,
conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
@@ -366,6 +390,24 @@ static int conn_call_deactivate_dcid(ngtcp2_conn *conn,
conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid);
}
+static int conn_call_stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *stream_user_data) {
+ int rv;
+
+ if (!conn->callbacks.stream_stop_sending) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stream_stop_sending(conn, stream_id, app_error_code,
+ conn->user_data, stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn,
ngtcp2_crypto_aead_ctx *aead_ctx) {
if (!aead_ctx->native_handle) {
@@ -389,6 +431,210 @@ conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn,
conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data);
}
+static int conn_call_client_initial(ngtcp2_conn *conn) {
+ int rv;
+
+ assert(conn->callbacks.client_initial);
+
+ rv = conn->callbacks.client_initial(conn, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data) {
+ int rv;
+
+ assert(conn->callbacks.get_path_challenge_data);
+
+ rv = conn->callbacks.get_path_challenge_data(conn, data, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_version_negotiation(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv) {
+ int rv;
+
+ if (!conn->callbacks.recv_version_negotiation) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_version_negotiation(conn, hd, sv, nsv,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) {
+ int rv;
+
+ assert(conn->callbacks.recv_retry);
+
+ rv = conn->callbacks.recv_retry(conn, hd, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int
+conn_call_recv_stateless_reset(ngtcp2_conn *conn,
+ const ngtcp2_pkt_stateless_reset *sr) {
+ int rv;
+
+ if (!conn->callbacks.recv_stateless_reset) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_stateless_reset(conn, sr, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_new_token(ngtcp2_conn *conn,
+ const ngtcp2_vec *token) {
+ int rv;
+
+ if (!conn->callbacks.recv_new_token) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_new_token(conn, token, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_handshake_confirmed(ngtcp2_conn *conn) {
+ int rv;
+
+ if (!conn->callbacks.handshake_confirmed) {
+ return 0;
+ }
+
+ rv = conn->callbacks.handshake_confirmed(conn, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_datagram(ngtcp2_conn *conn,
+ const ngtcp2_datagram *fr) {
+ const uint8_t *data;
+ size_t datalen;
+ int rv;
+ uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE;
+
+ if (!conn->callbacks.recv_datagram) {
+ return 0;
+ }
+
+ if (fr->datacnt) {
+ assert(fr->datacnt == 1);
+
+ data = fr->data->base;
+ datalen = fr->data->len;
+ } else {
+ data = NULL;
+ datalen = 0;
+ }
+
+ if (!conn_is_handshake_completed(conn)) {
+ flags |= NGTCP2_DATAGRAM_FLAG_EARLY;
+ }
+
+ rv = conn->callbacks.recv_datagram(conn, flags, data, datalen,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int
+conn_call_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret,
+ const uint8_t *current_tx_secret, size_t secretlen) {
+ int rv;
+
+ assert(conn->callbacks.update_key);
+
+ rv = conn->callbacks.update_key(
+ conn, rx_secret, tx_secret, rx_aead_ctx, rx_iv, tx_aead_ctx, tx_iv,
+ current_rx_secret, current_tx_secret, secretlen, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_version_negotiation(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *dcid) {
+ int rv;
+
+ assert(conn->callbacks.version_negotiation);
+
+ rv =
+ conn->callbacks.version_negotiation(conn, version, dcid, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_rx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) {
+ int rv;
+
+ if (!conn->callbacks.recv_rx_key) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_rx_key(conn, level, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_tx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) {
+ int rv;
+
+ if (!conn->callbacks.recv_tx_key) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_tx_key(conn, level, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
static int crypto_offset_less(const ngtcp2_ksl_key *lhs,
const ngtcp2_ksl_key *rhs) {
return *(int64_t *)lhs < *(int64_t *)rhs;
@@ -396,15 +642,13 @@ static int crypto_offset_less(const ngtcp2_ksl_key *lhs,
static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id,
ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log,
- ngtcp2_qlog *qlog, const ngtcp2_mem *mem) {
+ ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
int rv;
memset(pktns, 0, sizeof(*pktns));
- rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
pktns->tx.last_pkt_num = -1;
pktns->rx.max_pkt_num = -1;
@@ -415,27 +659,17 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id,
goto fail_acktr_init;
}
- rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0,
- NULL, mem);
- if (rv != 0) {
- goto fail_crypto_init;
- }
+ ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL,
+ NULL, mem);
- rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less,
- sizeof(uint64_t), mem);
- if (rv != 0) {
- goto fail_tx_frq_init;
- }
+ ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, sizeof(uint64_t),
+ mem);
ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log,
- qlog, mem);
+ qlog, rtb_entry_objalloc, frc_objalloc, mem);
return 0;
-fail_tx_frq_init:
- ngtcp2_strm_free(&pktns->crypto.strm);
-fail_crypto_init:
- ngtcp2_acktr_free(&pktns->acktr);
fail_acktr_init:
ngtcp2_gaptr_free(&pktns->rx.pngap);
@@ -444,7 +678,8 @@ fail_acktr_init:
static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id,
ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log,
- ngtcp2_qlog *qlog, const ngtcp2_mem *mem) {
+ ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
int rv;
*ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns));
@@ -452,7 +687,8 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id,
return NGTCP2_ERR_NOMEM;
}
- rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem);
+ rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, rtb_entry_objalloc,
+ frc_objalloc, mem);
if (rv != 0) {
ngtcp2_mem_free(mem, *ppktns);
}
@@ -481,13 +717,27 @@ static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) {
}
}
+static void delete_buf_chain(ngtcp2_buf_chain *bufchain,
+ const ngtcp2_mem *mem) {
+ ngtcp2_buf_chain *next;
+
+ for (; bufchain;) {
+ next = bufchain->next;
+ ngtcp2_buf_chain_del(bufchain, mem);
+ bufchain = next;
+ }
+}
+
static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
ngtcp2_frame_chain *frc;
ngtcp2_ksl_it it;
+ delete_buf_chain(pktns->crypto.tx.data, mem);
+
delete_buffed_pkts(pktns->rx.buffed_pkts, mem);
- ngtcp2_frame_chain_list_del(pktns->tx.frq, mem);
+ ngtcp2_frame_chain_list_objalloc_del(pktns->tx.frq, pktns->rtb.frc_objalloc,
+ mem);
ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem);
ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem);
@@ -495,7 +745,7 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);
ngtcp2_ksl_it_next(&it)) {
frc = ngtcp2_ksl_it_get(&it);
- ngtcp2_frame_chain_del(frc, mem);
+ ngtcp2_frame_chain_objalloc_del(frc, pktns->rtb.frc_objalloc, mem);
}
ngtcp2_ksl_free(&pktns->crypto.tx.frq);
@@ -524,6 +774,12 @@ static void cc_del(ngtcp2_cc *cc, ngtcp2_cc_algo cc_algo,
case NGTCP2_CC_ALGO_CUBIC:
ngtcp2_cc_cubic_cc_free(cc, mem);
break;
+ case NGTCP2_CC_ALGO_BBR:
+ ngtcp2_cc_bbr_cc_free(cc, mem);
+ break;
+ case NGTCP2_CC_ALGO_BBR2:
+ ngtcp2_cc_bbr2_cc_free(cc, mem);
+ break;
default:
break;
}
@@ -533,12 +789,12 @@ static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
return ngtcp2_cid_less(lhs, rhs);
}
-static int ts_retired_less(const ngtcp2_pq_entry *lhs,
+static int retired_ts_less(const ngtcp2_pq_entry *lhs,
const ngtcp2_pq_entry *rhs) {
const ngtcp2_scid *a = ngtcp2_struct_of(lhs, ngtcp2_scid, pe);
const ngtcp2_scid *b = ngtcp2_struct_of(rhs, ngtcp2_scid, pe);
- return a->ts_retired < b->ts_retired;
+ return a->retired_ts < b->retired_ts;
}
/*
@@ -559,6 +815,8 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn,
cstat->congestion_recovery_start_ts = UINT64_MAX;
cstat->bytes_in_flight = 0;
cstat->delivery_rate_sec = 0;
+ cstat->pacing_rate = 0.0;
+ cstat->send_quantum = SIZE_MAX;
}
/*
@@ -566,7 +824,7 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn,
* function
*/
static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) {
- // Initializes them with UINT64_MAX.
+ /* Initializes them with UINT64_MAX. */
memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time));
memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts));
}
@@ -605,10 +863,14 @@ static ngtcp2_duration compute_pto(ngtcp2_duration smoothed_rtt,
static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn,
ngtcp2_pktns *pktns) {
ngtcp2_duration initial_rtt = conn->local.settings.initial_rtt;
- ngtcp2_duration max_ack_delay =
- pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION
- ? conn->remote.transport_params.max_ack_delay
- : 0;
+ ngtcp2_duration max_ack_delay;
+
+ if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION &&
+ conn->remote.transport_params) {
+ max_ack_delay = conn->remote.transport_params->max_ack_delay;
+ } else {
+ max_ack_delay = 0;
+ }
return compute_pto(initial_rtt, initial_rtt / 2, max_ack_delay);
}
@@ -618,15 +880,24 @@ static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn,
static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn,
ngtcp2_pktns *pktns) {
ngtcp2_conn_stat *cstat = &conn->cstat;
- ngtcp2_duration max_ack_delay =
- pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION
- ? conn->remote.transport_params.max_ack_delay
- : 0;
+ ngtcp2_duration max_ack_delay;
+
+ if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION &&
+ conn->remote.transport_params) {
+ max_ack_delay = conn->remote.transport_params->max_ack_delay;
+ } else {
+ max_ack_delay = 0;
+ }
return compute_pto(cstat->smoothed_rtt, cstat->rttvar, max_ack_delay);
}
+ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns) {
+ return conn_compute_pto(conn, pktns);
+}
+
static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
- uint8_t *prtb_entry_flags, ngtcp2_pktns *pktns,
+ uint16_t *prtb_entry_flags, ngtcp2_pktns *pktns,
const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) {
assert(pi);
@@ -718,23 +989,103 @@ static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) {
pktns->tx.ecn.validation_pkt_lost = 0;
}
+/* server_default_other_versions is the default other_versions field
+ sent by server. */
+static uint8_t server_default_other_versions[] = {0, 0, 0, 1};
+
+/*
+ * other_versions_new allocates new buffer, and writes |versions| of
+ * length |versionslen| in network byte order, suitable for sending in
+ * other_versions field of version_information QUIC transport
+ * parameter. The pointer to the allocated buffer is assigned to
+ * |*pbuf|.
+ *
+ * This function returns 0 if it succeeds, or one of the negative
+ * error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int other_versions_new(uint8_t **pbuf, const uint32_t *versions,
+ size_t versionslen, const ngtcp2_mem *mem) {
+ size_t i;
+ uint8_t *buf = ngtcp2_mem_malloc(mem, sizeof(uint32_t) * versionslen);
+
+ if (buf == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ *pbuf = buf;
+
+ for (i = 0; i < versionslen; ++i) {
+ buf = ngtcp2_put_uint32be(buf, versions[i]);
+ }
+
+ return 0;
+}
+
+static void
+conn_set_local_transport_params(ngtcp2_conn *conn,
+ const ngtcp2_transport_params *params) {
+ ngtcp2_transport_params *p = &conn->local.transport_params;
+ uint32_t chosen_version = p->version_info.chosen_version;
+
+ *p = *params;
+
+ /* grease_quic_bit is always enabled. */
+ p->grease_quic_bit = 1;
+
+ if (conn->server) {
+ p->version_info.chosen_version = chosen_version;
+ } else {
+ p->version_info.chosen_version = conn->client_chosen_version;
+ }
+ p->version_info.other_versions = conn->vneg.other_versions;
+ p->version_info.other_versionslen = conn->vneg.other_versionslen;
+ p->version_info_present = 1;
+}
+
static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
const ngtcp2_cid *scid, const ngtcp2_path *path,
- uint32_t version, const ngtcp2_callbacks *callbacks,
+ uint32_t client_chosen_version, int callbacks_version,
+ const ngtcp2_callbacks *callbacks, int settings_version,
const ngtcp2_settings *settings,
+ int transport_params_version,
const ngtcp2_transport_params *params,
const ngtcp2_mem *mem, void *user_data, int server) {
int rv;
ngtcp2_scid *scident;
uint8_t *buf;
+ uint8_t fixed_bit_byte;
+ size_t i;
+ uint32_t *preferred_versions;
+ (void)callbacks_version;
+ (void)settings_version;
+ (void)transport_params_version;
assert(settings->max_window <= NGTCP2_MAX_VARINT);
assert(settings->max_stream_window <= NGTCP2_MAX_VARINT);
+ assert(settings->max_udp_payload_size);
+ assert(settings->max_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE);
assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE);
assert(params->initial_max_data <= NGTCP2_MAX_VARINT);
assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT);
assert(params->initial_max_stream_data_bidi_remote <= NGTCP2_MAX_VARINT);
assert(params->initial_max_stream_data_uni <= NGTCP2_MAX_VARINT);
+ assert(server || callbacks->client_initial);
+ assert(!server || callbacks->recv_client_initial);
+ assert(callbacks->recv_crypto_data);
+ assert(callbacks->encrypt);
+ assert(callbacks->decrypt);
+ assert(callbacks->hp_mask);
+ assert(server || callbacks->recv_retry);
+ assert(callbacks->rand);
+ assert(callbacks->get_new_connection_id);
+ assert(callbacks->update_key);
+ assert(callbacks->delete_crypto_aead_ctx);
+ assert(callbacks->delete_crypto_cipher_ctx);
+ assert(callbacks->get_path_challenge_data);
+ assert(!server || !ngtcp2_is_reserved_version(client_chosen_version));
if (mem == NULL) {
mem = ngtcp2_mem_default();
@@ -746,60 +1097,31 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
goto fail_conn;
}
- rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.bound,
- NGTCP2_MAX_BOUND_DCID_POOL_SIZE, sizeof(ngtcp2_dcid),
- mem);
- if (rv != 0) {
- goto fail_dcid_bound_init;
- }
+ ngtcp2_objalloc_frame_chain_init(&(*pconn)->frc_objalloc, 64, mem);
+ ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 64, mem);
+ ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 64, mem);
- rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE,
- sizeof(ngtcp2_dcid), mem);
- if (rv != 0) {
- goto fail_dcid_unused_init;
- }
+ ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound);
- rv =
- ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
- sizeof(ngtcp2_dcid), mem);
- if (rv != 0) {
- goto fail_dcid_retired_init;
- }
+ ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused);
- rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem);
- if (rv != 0) {
- goto fail_seqgap_init;
- }
+ ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired);
- rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem);
- if (rv != 0) {
- goto fail_scid_set_init;
- }
+ ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem);
- ngtcp2_pq_init(&(*pconn)->scid.used, ts_retired_less, mem);
+ ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem);
- rv = ngtcp2_map_init(&(*pconn)->strms, mem);
- if (rv != 0) {
- goto fail_strms_init;
- }
+ ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem);
+
+ ngtcp2_map_init(&(*pconn)->strms, mem);
ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem);
- rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem);
- if (rv != 0) {
- goto fail_remote_bidi_idtr_init;
- }
+ ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem);
- rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem);
- if (rv != 0) {
- goto fail_remote_uni_idtr_init;
- }
+ ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem);
- rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4,
- sizeof(ngtcp2_path_challenge_entry), mem);
- if (rv != 0) {
- goto fail_rx_path_challenge_init;
- }
+ ngtcp2_static_ringbuf_path_challenge_init(&(*pconn)->rx.path_challenge);
ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf,
settings->initial_ts, user_data);
@@ -808,17 +1130,18 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
if ((*pconn)->qlog.write) {
buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN);
if (buf == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
goto fail_qlog_buf;
}
ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN);
}
(*pconn)->local.settings = *settings;
- (*pconn)->local.transport_params = *params;
if (settings->token.len) {
buf = ngtcp2_mem_malloc(mem, settings->token.len);
if (buf == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
goto fail_token;
}
memcpy(buf, settings->token.base, settings->token.len);
@@ -828,8 +1151,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
(*pconn)->local.settings.token.len = 0;
}
- if (settings->max_udp_payload_size == 0) {
- (*pconn)->local.settings.max_udp_payload_size = NGTCP2_DEFAULT_MAX_PKTLEN;
+ if (!(*pconn)->local.settings.original_version) {
+ (*pconn)->local.settings.original_version = client_chosen_version;
}
conn_reset_conn_stat(*pconn, &(*pconn)->cstat);
@@ -854,29 +1177,43 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
goto fail_cc_init;
}
break;
- case NGTCP2_CC_ALGO_CUSTOM:
- assert(settings->cc);
- (*pconn)->cc = *settings->cc;
- (*pconn)->cc.ccb->log = &(*pconn)->log;
+ case NGTCP2_CC_ALGO_BBR:
+ rv = ngtcp2_cc_bbr_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat,
+ &(*pconn)->rst, settings->initial_ts,
+ callbacks->rand, &settings->rand_ctx, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
+ break;
+ case NGTCP2_CC_ALGO_BBR2:
+ rv = ngtcp2_cc_bbr2_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat,
+ &(*pconn)->rst, settings->initial_ts,
+ callbacks->rand, &settings->rand_ctx, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
break;
default:
assert(0);
}
rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst,
- &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem);
+ &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+ &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
if (rv != 0) {
goto fail_in_pktns_init;
}
rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst,
- &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem);
+ &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+ &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
if (rv != 0) {
goto fail_hs_pktns_init;
}
rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst,
- &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem);
+ &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+ &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
if (rv != 0) {
goto fail_pktns_init;
}
@@ -889,7 +1226,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
/* Set stateless reset token later if it is available in the local
transport parameters */
- ngtcp2_scid_init(scident, 0, scid, NULL);
+ ngtcp2_scid_init(scident, 0, scid);
rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident);
if (rv != 0) {
@@ -899,22 +1236,111 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
scident = NULL;
ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL);
- ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path);
+ ngtcp2_dcid_set_path(&(*pconn)->dcid.current, path);
rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1);
if (rv != 0) {
goto fail_seqgap_push;
}
+ if (settings->preferred_versionslen) {
+ if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+ for (i = 0; i < settings->preferred_versionslen; ++i) {
+ if (settings->preferred_versions[i] == client_chosen_version) {
+ break;
+ }
+ }
+
+ assert(i < settings->preferred_versionslen);
+ }
+
+ preferred_versions = ngtcp2_mem_malloc(
+ mem, sizeof(uint32_t) * settings->preferred_versionslen);
+ if (preferred_versions == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail_preferred_versions;
+ }
+
+ for (i = 0; i < settings->preferred_versionslen; ++i) {
+ assert(ngtcp2_is_supported_version(settings->preferred_versions[i]));
+
+ preferred_versions[i] = settings->preferred_versions[i];
+ }
+
+ (*pconn)->vneg.preferred_versions = preferred_versions;
+ (*pconn)->vneg.preferred_versionslen = settings->preferred_versionslen;
+ }
+
+ if (settings->other_versionslen) {
+ if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+ for (i = 0; i < settings->other_versionslen; ++i) {
+ if (settings->other_versions[i] == client_chosen_version) {
+ break;
+ }
+ }
+
+ assert(i < settings->other_versionslen);
+ }
+
+ for (i = 0; i < settings->other_versionslen; ++i) {
+ assert(ngtcp2_is_reserved_version(settings->other_versions[i]) ||
+ ngtcp2_is_supported_version(settings->other_versions[i]));
+ }
+
+ rv = other_versions_new(&buf, settings->other_versions,
+ settings->other_versionslen, mem);
+ if (rv != 0) {
+ goto fail_other_versions;
+ }
+
+ (*pconn)->vneg.other_versions = buf;
+ (*pconn)->vneg.other_versionslen =
+ sizeof(uint32_t) * settings->other_versionslen;
+ } else if (server) {
+ if (settings->preferred_versionslen) {
+ rv = other_versions_new(&buf, settings->preferred_versions,
+ settings->preferred_versionslen, mem);
+ if (rv != 0) {
+ goto fail_other_versions;
+ }
+
+ (*pconn)->vneg.other_versions = buf;
+ (*pconn)->vneg.other_versionslen =
+ sizeof(uint32_t) * settings->preferred_versionslen;
+ } else {
+ (*pconn)->vneg.other_versions = server_default_other_versions;
+ (*pconn)->vneg.other_versionslen = sizeof(server_default_other_versions);
+ }
+ } else if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+ rv = other_versions_new(&buf, &client_chosen_version, 1, mem);
+ if (rv != 0) {
+ goto fail_other_versions;
+ }
+
+ (*pconn)->vneg.other_versions = buf;
+ (*pconn)->vneg.other_versionslen = sizeof(uint32_t);
+ }
+
+ (*pconn)->client_chosen_version = client_chosen_version;
+
+ conn_set_local_transport_params(*pconn, params);
+
+ callbacks->rand(&fixed_bit_byte, 1, &settings->rand_ctx);
+ if (fixed_bit_byte & 1) {
+ (*pconn)->flags |= NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT;
+ }
+
+ (*pconn)->keep_alive.last_ts = UINT64_MAX;
+
(*pconn)->server = server;
(*pconn)->oscid = *scid;
(*pconn)->callbacks = *callbacks;
- (*pconn)->version = version;
(*pconn)->mem = mem;
(*pconn)->user_data = user_data;
(*pconn)->idle_ts = settings->initial_ts;
(*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX;
(*pconn)->tx.last_max_data_ts = UINT64_MAX;
+ (*pconn)->tx.pacing.next_ts = UINT64_MAX;
(*pconn)->early.discard_started_ts = UINT64_MAX;
conn_reset_ecn_validation_state(*pconn);
@@ -924,12 +1350,13 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
return 0;
+fail_other_versions:
+ ngtcp2_mem_free(mem, (*pconn)->vneg.preferred_versions);
+fail_preferred_versions:
fail_seqgap_push:
fail_scid_set_insert:
ngtcp2_mem_free(mem, scident);
fail_scident:
- ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base);
-fail_token:
pktns_free(&(*pconn)->pktns, mem);
fail_pktns_init:
pktns_del((*pconn)->hs_pktns, mem);
@@ -938,41 +1365,36 @@ fail_hs_pktns_init:
fail_in_pktns_init:
cc_del(&(*pconn)->cc, settings->cc_algo, mem);
fail_cc_init:
+ ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base);
+fail_token:
ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin);
fail_qlog_buf:
- ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge);
-fail_rx_path_challenge_init:
ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr);
-fail_remote_uni_idtr_init:
ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr);
-fail_remote_bidi_idtr_init:
ngtcp2_map_free(&(*pconn)->strms);
-fail_strms_init:
delete_scid(&(*pconn)->scid.set, mem);
ngtcp2_ksl_free(&(*pconn)->scid.set);
-fail_scid_set_init:
ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap);
-fail_seqgap_init:
- ngtcp2_ringbuf_free(&(*pconn)->dcid.retired);
-fail_dcid_retired_init:
- ngtcp2_ringbuf_free(&(*pconn)->dcid.unused);
-fail_dcid_unused_init:
- ngtcp2_ringbuf_free(&(*pconn)->dcid.bound);
-fail_dcid_bound_init:
+ ngtcp2_objalloc_free(&(*pconn)->strm_objalloc);
+ ngtcp2_objalloc_free(&(*pconn)->rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&(*pconn)->frc_objalloc);
ngtcp2_mem_free(mem, *pconn);
fail_conn:
return rv;
}
-int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid, const ngtcp2_path *path,
- uint32_t version, const ngtcp2_callbacks *callbacks,
- const ngtcp2_settings *settings,
- const ngtcp2_transport_params *params,
- const ngtcp2_mem *mem, void *user_data) {
+int ngtcp2_conn_client_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data) {
int rv;
- rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params,
- mem, user_data, 0);
+
+ rv = conn_new(pconn, dcid, scid, path, client_chosen_version,
+ callbacks_version, callbacks, settings_version, settings,
+ transport_params_version, params, mem, user_data, 0);
if (rv != 0) {
return rv;
}
@@ -990,18 +1412,22 @@ int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
return 0;
}
-int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid, const ngtcp2_path *path,
- uint32_t version, const ngtcp2_callbacks *callbacks,
- const ngtcp2_settings *settings,
- const ngtcp2_transport_params *params,
- const ngtcp2_mem *mem, void *user_data) {
+int ngtcp2_conn_server_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data) {
int rv;
- rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params,
- mem, user_data, 1);
+
+ rv = conn_new(pconn, dcid, scid, path, client_chosen_version,
+ callbacks_version, callbacks, settings_version, settings,
+ transport_params_version, params, mem, user_data, 1);
if (rv != 0) {
return rv;
}
+
(*pconn)->state = NGTCP2_CS_SERVER_INITIAL;
(*pconn)->local.bidi.next_stream_id = 1;
(*pconn)->local.uni.next_stream_id = 3;
@@ -1029,22 +1455,37 @@ static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) {
* sent to the given stream. |len| might be shorted because of
* available flow control credits.
*/
-static size_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm,
- size_t len) {
+static uint64_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t len) {
uint64_t fc_credits = conn_fc_credits(conn, strm);
- return (size_t)ngtcp2_min((uint64_t)len, fc_credits);
+ return ngtcp2_min(len, fc_credits);
}
-static int delete_strms_each(ngtcp2_map_entry *ent, void *ptr) {
- const ngtcp2_mem *mem = ptr;
- ngtcp2_strm *s = ngtcp2_struct_of(ent, ngtcp2_strm, me);
+static int delete_strms_each(void *data, void *ptr) {
+ ngtcp2_conn *conn = ptr;
+ ngtcp2_strm *s = data;
ngtcp2_strm_free(s);
- ngtcp2_mem_free(mem, s);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s);
return 0;
}
+static void conn_vneg_crypto_free(ngtcp2_conn *conn) {
+ if (conn->vneg.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx);
+
+ if (conn->vneg.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx);
+
+ ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem);
+}
+
void ngtcp2_conn_del(ngtcp2_conn *conn) {
if (conn == NULL) {
return;
@@ -1111,6 +1552,16 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) {
conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx);
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+ ngtcp2_transport_params_del(conn->remote.pending_transport_params, conn->mem);
+
+ conn_vneg_crypto_free(conn);
+
+ ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions);
+ if (conn->vneg.other_versions != server_default_other_versions) {
+ ngtcp2_mem_free(conn->mem, conn->vneg.other_versions);
+ }
+
ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base);
ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base);
ngtcp2_mem_free(conn->mem, conn->local.settings.token.base);
@@ -1128,24 +1579,26 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) {
ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin);
- ngtcp2_ringbuf_free(&conn->rx.path_challenge);
-
+ ngtcp2_pmtud_del(conn->pmtud);
ngtcp2_pv_del(conn->pv);
+ ngtcp2_mem_free(conn->mem, conn->rx.ccerr.reason);
+
ngtcp2_idtr_free(&conn->remote.uni.idtr);
ngtcp2_idtr_free(&conn->remote.bidi.idtr);
ngtcp2_mem_free(conn->mem, conn->tx.ack);
ngtcp2_pq_free(&conn->tx.strmq);
- ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn->mem);
+ ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn);
ngtcp2_map_free(&conn->strms);
ngtcp2_pq_free(&conn->scid.used);
delete_scid(&conn->scid.set, conn->mem);
ngtcp2_ksl_free(&conn->scid.set);
ngtcp2_gaptr_free(&conn->dcid.seqgap);
- ngtcp2_ringbuf_free(&conn->dcid.retired);
- ngtcp2_ringbuf_free(&conn->dcid.unused);
- ngtcp2_ringbuf_free(&conn->dcid.bound);
+
+ ngtcp2_objalloc_free(&conn->strm_objalloc);
+ ngtcp2_objalloc_free(&conn->rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&conn->frc_objalloc);
ngtcp2_mem_free(conn->mem, conn);
}
@@ -1284,7 +1737,7 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr,
ack->first_ack_blklen = 0;
}
- if (type == NGTCP2_PKT_SHORT) {
+ if (type == NGTCP2_PKT_1RTT) {
ack->ack_delay_unscaled = ts - largest_ack_ts;
ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS /
(1ULL << ack_delay_exponent);
@@ -1412,11 +1865,11 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) {
ngtcp2_rtb *rtb = &pktns->rtb;
int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num;
- if (NGTCP2_MAX_PKT_NUM / 2 <= pkt_num) {
+ if (NGTCP2_MAX_PKT_NUM / 2 < n) {
return 4;
}
- n = n * 2 + 1;
+ n = n * 2 - 1;
if (n > 0xffffff) {
return 4;
@@ -1431,15 +1884,27 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) {
}
/*
+ * conn_get_cwnd returns cwnd for the current path.
+ */
+static uint64_t conn_get_cwnd(ngtcp2_conn *conn) {
+ return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)
+ ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size)
+ : conn->cstat.cwnd;
+}
+
+/*
* conn_cwnd_is_zero returns nonzero if the number of bytes the local
* endpoint can sent at this time is zero.
*/
static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) {
uint64_t bytes_in_flight = conn->cstat.bytes_in_flight;
- uint64_t cwnd =
- conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)
- ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size)
- : conn->cstat.cwnd;
+ uint64_t cwnd = conn_get_cwnd(conn);
+
+ if (bytes_in_flight >= cwnd) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "cwnd limited bytes_in_flight=%lu cwnd=%lu",
+ bytes_in_flight, cwnd);
+ }
return bytes_in_flight >= cwnd;
}
@@ -1447,11 +1912,12 @@ static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) {
/*
* conn_retry_early_payloadlen returns the estimated wire length of
* the first STREAM frame of 0-RTT packet which should be
- * retransmitted due to Retry frame
+ * retransmitted due to Retry packet.
*/
-static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) {
+static uint64_t conn_retry_early_payloadlen(ngtcp2_conn *conn) {
ngtcp2_frame_chain *frc;
ngtcp2_strm *strm;
+ uint64_t len;
if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) {
return 0;
@@ -1465,8 +1931,13 @@ static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) {
}
frc = ngtcp2_strm_streamfrq_top(strm);
- return ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) +
- NGTCP2_STREAM_OVERHEAD;
+
+ len = ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) +
+ NGTCP2_STREAM_OVERHEAD;
+
+ /* Take the min because in conn_should_pad_pkt we take max in
+ order to deal with unbreakable DATAGRAM. */
+ return ngtcp2_min(len, NGTCP2_MIN_COALESCED_PAYLOADLEN);
}
return 0;
@@ -1479,7 +1950,7 @@ static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) {
for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);
ngtcp2_ksl_it_next(&it)) {
frc = ngtcp2_ksl_it_get(&it);
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
}
ngtcp2_ksl_clear(&pktns->crypto.tx.frq);
}
@@ -1496,7 +1967,7 @@ static uint64_t conn_cryptofrq_unacked_offset(ngtcp2_conn *conn,
ngtcp2_range gap;
ngtcp2_rtb *rtb = &pktns->rtb;
ngtcp2_ksl_it it;
- size_t datalen;
+ uint64_t datalen;
(void)conn;
@@ -1539,7 +2010,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
frc = ngtcp2_ksl_it_get(&it);
fr = &frc->fr.crypto;
- ngtcp2_ksl_remove(&pktns->crypto.tx.frq, &it, &fr->offset);
+ ngtcp2_ksl_remove_hint(&pktns->crypto.tx.frq, &it, &it, &fr->offset);
idx = 0;
offset = fr->offset;
@@ -1561,7 +2032,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
}
if (idx == fr->datacnt) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
continue;
}
@@ -1600,10 +2071,10 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
return 0;
}
- rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx,
- conn->mem);
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, fr->datacnt - end_idx, &conn->frc_objalloc, conn->mem);
if (rv != 0) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -1622,8 +2093,8 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(nfrc, conn->mem);
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -1656,7 +2127,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
ngtcp2_frame_chain *frc, *nfrc;
int rv;
size_t nmerged;
- size_t datalen;
+ uint64_t datalen;
ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT];
ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT];
size_t acnt, bcnt;
@@ -1684,10 +2155,11 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
assert(acnt > 0);
assert(bcnt > 0);
- rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem);
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, bcnt, &conn->frc_objalloc, conn->mem);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -1700,15 +2172,16 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(nfrc, conn->mem);
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
- rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem);
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, acnt, &conn->frc_objalloc, conn->mem);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -1717,14 +2190,14 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
nfr->datacnt = acnt;
ngtcp2_vec_copy(nfr->data, a, acnt);
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
*pfrc = nfrc;
return 0;
}
- left -= datalen;
+ left -= (size_t)datalen;
ngtcp2_vec_copy(a, fr->data, fr->datacnt);
acnt = fr->datacnt;
@@ -1742,7 +2215,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
if (nfrc == NULL) {
@@ -1757,8 +2230,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(nfrc, conn->mem);
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
break;
@@ -1768,7 +2241,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
left -= nmerged;
if (nfr->datacnt == 0) {
- ngtcp2_frame_chain_del(nfrc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
continue;
}
@@ -1776,8 +2249,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
- ngtcp2_frame_chain_del(nfrc, conn->mem);
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -1794,9 +2267,10 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
assert(acnt > fr->datacnt);
- rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem);
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, acnt, &conn->frc_objalloc, conn->mem);
if (rv != 0) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -1805,7 +2279,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
nfr->datacnt = acnt;
ngtcp2_vec_copy(nfr->data, a, acnt);
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
*pfrc = nfrc;
@@ -1866,59 +2340,74 @@ static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used,
/*
* conn_should_pad_pkt returns nonzero if the packet should be padded.
* |type| is the type of packet. |left| is the space left in packet
- * buffer. |early_datalen| is the number of bytes which will be sent
- * in the next, coalesced 0-RTT packet.
+ * buffer. |write_datalen| is the number of bytes which will be sent
+ * in the next, coalesced 0-RTT or 1RTT packet.
*/
static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left,
- size_t early_datalen, int ack_eliciting) {
- size_t min_payloadlen;
+ uint64_t write_datalen, int ack_eliciting,
+ int require_padding) {
+ uint64_t min_payloadlen;
- if (conn->server) {
- if (type != NGTCP2_PKT_INITIAL || !ack_eliciting) {
- return 0;
- }
+ if (type == NGTCP2_PKT_INITIAL) {
+ if (conn->server) {
+ if (!ack_eliciting) {
+ return 0;
+ }
- if (conn->hs_pktns->crypto.tx.ckm &&
- (conn->hs_pktns->rtb.probe_pkt_left ||
- ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) ||
- !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) {
- /* If we have something to send in Handshake packet, then add
- PADDING in Handshake packet. */
- min_payloadlen = 128;
+ if (conn->hs_pktns->crypto.tx.ckm &&
+ (conn->hs_pktns->rtb.probe_pkt_left ||
+ ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) ||
+ !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) {
+ /* If we have something to send in Handshake packet, then add
+ PADDING in Handshake packet. */
+ min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN;
+ } else {
+ return 1;
+ }
} else {
- return 1;
+ if (conn->hs_pktns->crypto.tx.ckm &&
+ (conn->hs_pktns->rtb.probe_pkt_left ||
+ ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) ||
+ !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) {
+ /* If we have something to send in Handshake packet, then add
+ PADDING in Handshake packet. */
+ min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN;
+ } else if ((!conn->early.ckm && !conn->pktns.crypto.tx.ckm) ||
+ write_datalen == 0) {
+ return 1;
+ } else {
+ /* If we have something to send in 0RTT or 1RTT packet, then
+ add PADDING in that packet. Take maximum in case that
+ write_datalen includes DATAGRAM which cannot be split. */
+ min_payloadlen =
+ ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ }
}
} else {
- if (type == NGTCP2_PKT_HANDSHAKE) {
- return conn->in_pktns != NULL;
- }
-
- if (conn->hs_pktns->crypto.tx.ckm &&
- (conn->hs_pktns->rtb.probe_pkt_left ||
- ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) ||
- !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) {
- /* If we have something to send in Handshake packet, then add
- PADDING in Handshake packet. */
- min_payloadlen = 128;
- } else if (!conn->early.ckm || early_datalen == 0) {
+ assert(type == NGTCP2_PKT_HANDSHAKE);
+
+ if (!require_padding) {
+ return 0;
+ }
+
+ if (!conn->pktns.crypto.tx.ckm || write_datalen == 0) {
return 1;
- } else {
- /* If we have something to send in 0RTT packet, then add PADDING
- in 0RTT packet. */
- min_payloadlen = ngtcp2_min(early_datalen, 128);
}
+
+ min_payloadlen = ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
}
+ /* TODO the next packet type should be taken into account */
return left <
/* TODO Assuming that pkt_num is encoded in 1 byte. */
NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen +
- conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ +
- min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD;
+ conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen +
+ NGTCP2_MAX_AEAD_OVERHEAD;
}
static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
conn->idle_ts = ts;
- conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE;
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE;
}
static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
@@ -1926,14 +2415,110 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
conn->flags |= NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE;
}
-/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00
-/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet should
- be padded */
-#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01
-/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come
- and it should be encoded into the current packet. */
-#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02
+/*
+ * conn_keep_alive_enabled returns nonzero if keep-alive is enabled.
+ */
+static int conn_keep_alive_enabled(ngtcp2_conn *conn) {
+ return conn->keep_alive.last_ts != UINT64_MAX && conn->keep_alive.timeout;
+}
+
+/*
+ * conn_keep_alive_expired returns nonzero if keep-alive timer has
+ * expired.
+ */
+static int conn_keep_alive_expired(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ return conn_keep_alive_enabled(conn) &&
+ conn->keep_alive.last_ts + conn->keep_alive.timeout <= ts;
+}
+
+/*
+ * conn_keep_alive_expiry returns the expiry time of keep-alive timer.
+ */
+static ngtcp2_tstamp conn_keep_alive_expiry(ngtcp2_conn *conn) {
+ if ((conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) ||
+ !conn_keep_alive_enabled(conn)) {
+ return UINT64_MAX;
+ }
+
+ return conn->keep_alive.last_ts + conn->keep_alive.timeout;
+}
+
+/*
+ * conn_cancel_expired_keep_alive_timer cancels the expired keep-alive
+ * timer.
+ */
+static void conn_cancel_expired_keep_alive_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) &&
+ conn_keep_alive_expired(conn, ts)) {
+ conn->flags |= NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED;
+ }
+}
+
+/*
+ * conn_update_keep_alive_last_ts updates the base time point of
+ * keep-alive timer.
+ */
+static void conn_update_keep_alive_last_ts(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ conn->keep_alive.last_ts = ts;
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED;
+}
+
+void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
+ ngtcp2_duration timeout) {
+ conn->keep_alive.timeout = timeout;
+}
+
+/*
+ * NGTCP2_PKT_PACING_OVERHEAD defines overhead of userspace event
+ * loop. Packet pacing might require sub milliseconds packet spacing,
+ * but userspace event loop might not offer such precision.
+ * Typically, if delay is 0.5 microseconds, the actual delay after
+ * which we can send packet is well over 1 millisecond when event loop
+ * is involved (which includes other stuff, like reading packets etc
+ * in a typical single threaded use case).
+ */
+#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS
+
+static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ if (conn->tx.pacing.next_ts == UINT64_MAX) {
+ return;
+ }
+
+ if (conn->tx.pacing.next_ts > ts + NGTCP2_PKT_PACING_OVERHEAD) {
+ return;
+ }
+
+ conn->tx.pacing.next_ts = UINT64_MAX;
+}
+
+static int conn_pacing_pkt_tx_allowed(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ return conn->tx.pacing.next_ts == UINT64_MAX ||
+ conn->tx.pacing.next_ts <= ts + NGTCP2_PKT_PACING_OVERHEAD;
+}
+
+static uint8_t conn_pkt_flags(ngtcp2_conn *conn) {
+ if (conn->remote.transport_params &&
+ conn->remote.transport_params->grease_quic_bit &&
+ (conn->flags & NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT)) {
+ return NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
+ }
+
+ return NGTCP2_PKT_FLAG_NONE;
+}
+
+static uint8_t conn_pkt_flags_long(ngtcp2_conn *conn) {
+ return NGTCP2_PKT_FLAG_LONG_FORM | conn_pkt_flags(conn);
+}
+
+static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) {
+ return (uint8_t)(conn_pkt_flags(conn) | ((conn->pktns.crypto.tx.ckm->flags &
+ NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)
+ ? NGTCP2_PKT_FLAG_KEY_PHASE
+ : NGTCP2_PKT_FLAG_NONE));
+}
/*
* conn_write_handshake_pkt writes handshake packet in the buffer
@@ -1941,6 +2526,9 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
* packet type. It should be either NGTCP2_PKT_INITIAL or
* NGTCP2_PKT_HANDSHAKE_PKT.
*
+ * |write_datalen| is the minimum length of application data ready to
+ * send in subsequent 0RTT or 1RTT packet.
+ *
* This function returns the number of bytes written in |dest| if it
* succeeds, or one of the following negative error codes:
*
@@ -1952,7 +2540,7 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
static ngtcp2_ssize
conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
size_t destlen, uint8_t type, uint8_t flags,
- size_t early_datalen, ngtcp2_tstamp ts) {
+ uint64_t write_datalen, ngtcp2_tstamp ts) {
int rv;
ngtcp2_ppe ppe;
ngtcp2_pkt_hd hd;
@@ -1964,13 +2552,14 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
ngtcp2_rtb_entry *rtbent;
ngtcp2_pktns *pktns;
size_t left;
- uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
+ uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0;
int pkt_empty = 1;
int padded = 0;
int hd_logged = 0;
uint64_t crypto_offset;
ngtcp2_ssize num_reclaimed;
+ uint32_t version;
switch (type) {
case NGTCP2_PKT_INITIAL:
@@ -1979,28 +2568,41 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
}
assert(conn->in_pktns->crypto.tx.ckm);
pktns = conn->in_pktns;
+ version = conn->negotiated_version ? conn->negotiated_version
+ : conn->client_chosen_version;
+ if (version == conn->client_chosen_version) {
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+ } else {
+ assert(conn->vneg.version == version);
+
+ cc.ckm = conn->vneg.tx.ckm;
+ cc.hp_ctx = conn->vneg.tx.hp_ctx;
+ }
break;
case NGTCP2_PKT_HANDSHAKE:
if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) {
return 0;
}
pktns = conn->hs_pktns;
+ version = conn->negotiated_version;
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
break;
default:
assert(0);
+ abort();
}
cc.aead = pktns->crypto.ctx.aead;
cc.hp = pktns->crypto.ctx.hp;
- cc.ckm = pktns->crypto.tx.ckm;
- cc.hp_ctx = pktns->crypto.tx.hp_ctx;
cc.encrypt = conn->callbacks.encrypt;
cc.hp_mask = conn->callbacks.hp_mask;
- ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, type,
+ ngtcp2_pkt_hd_init(&hd, conn_pkt_flags_long(conn), type,
&conn->dcid.current.cid, &conn->oscid,
pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
- conn->version, 0);
+ version, 0);
if (!conn->server && type == NGTCP2_PKT_INITIAL &&
conn->local.settings.token.len) {
@@ -2023,7 +2625,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
/* ack_delay = */ 0,
NGTCP2_DEFAULT_ACK_DELAY_EXPONENT);
if (rv != 0) {
- ngtcp2_frame_chain_list_del(frq, conn->mem);
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -2038,10 +2640,10 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
}
}
- /* Server requires at least NGTCP2_DEFAULT_MAX_PKTLEN bytes in order
- to send ack-eliciting Initial packet. */
+ /* Server requires at least NGTCP2_MAX_UDP_PAYLOAD_SIZE bytes in
+ order to send ack-eliciting Initial packet. */
if (!conn->server || type != NGTCP2_PKT_INITIAL ||
- destlen >= NGTCP2_DEFAULT_MAX_PKTLEN) {
+ destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
build_pkt:
for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) {
left = ngtcp2_ppe_left(&ppe);
@@ -2060,7 +2662,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_list_del(frq, conn->mem);
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+ conn->mem);
return rv;
}
@@ -2078,15 +2681,16 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
}
if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) {
- num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns,
- pktns->rtb.probe_pkt_left + 1);
+ num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1);
if (num_reclaimed < 0) {
- ngtcp2_frame_chain_list_del(frq, conn->mem);
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+ conn->mem);
return rv;
}
if (num_reclaimed) {
@@ -2098,21 +2702,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
send any probe packet. Client needs to send probe packets
until it knows that server has completed address validation or
handshake has been confirmed. */
- if (pktns->rtb.num_retransmittable == 0 &&
+ if (pktns->rtb.num_pto_eliciting == 0 &&
(conn->server ||
(conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
pktns->rtb.probe_pkt_left = 0;
ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ /* TODO If packet is empty, we should return now if cwnd is
+ zero. */
}
}
- /* Don't send any PING frame if client Initial has not been
- acknowledged yet. */
if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
- pktns->rtb.probe_pkt_left &&
- (type != NGTCP2_PKT_INITIAL ||
- ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) {
+ pktns->rtb.probe_pkt_left) {
lfr.type = NGTCP2_FRAME_PING;
rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
@@ -2129,7 +2731,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
/* The intention of smaller limit is get more chance to measure
RTT samples in early phase. */
- if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) {
+ if (pktns->tx.num_non_ack_pkt >= 1) {
lfr.type = NGTCP2_FRAME_PING;
rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
@@ -2154,10 +2756,10 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
/* If we cannot write another packet, then we need to add padding to
Initial here. */
- if (require_padding ||
- conn_should_pad_pkt(
- conn, type, ngtcp2_ppe_left(&ppe), early_datalen,
- (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0)) {
+ if (conn_should_pad_pkt(
+ conn, type, ngtcp2_ppe_left(&ppe), write_datalen,
+ (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0,
+ require_padding)) {
lfr.type = NGTCP2_FRAME_PADDING;
lfr.padding.len = ngtcp2_ppe_padding(&ppe);
} else {
@@ -2174,7 +2776,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
spktlen = ngtcp2_ppe_final(&ppe, NULL);
if (spktlen < 0) {
assert(ngtcp2_err_is_fatal((int)spktlen));
- ngtcp2_frame_chain_list_del(frq, conn->mem);
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
return spktlen;
}
@@ -2185,17 +2787,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts);
}
- rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen,
- rtb_entry_flags, conn->mem);
+ rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, frq, ts, (size_t)spktlen,
+ rtb_entry_flags,
+ &conn->rtb_entry_objalloc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_list_del(frq, conn->mem);
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
return rv;
}
rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
if (rv != 0) {
- ngtcp2_rtb_entry_del(rtbent, conn->mem);
+ ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc,
+ &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -2212,8 +2816,12 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
--pktns->rtb.probe_pkt_left;
}
+ conn_update_keep_alive_last_ts(conn, ts);
+
conn->dcid.current.bytes_sent += (uint64_t)spktlen;
+ conn->tx.pacing.pktlen += (size_t)spktlen;
+
ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
++pktns->tx.last_pkt_num;
@@ -2258,13 +2866,14 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
ack_delay = 0;
ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
break;
- case NGTCP2_PKT_SHORT:
+ case NGTCP2_PKT_1RTT:
pktns = &conn->pktns;
ack_delay = conn_compute_ack_delay(conn);
ack_delay_exponent = conn->local.transport_params.ack_delay_exponent;
break;
default:
assert(0);
+ abort();
}
if (!pktns->crypto.tx.ckm) {
@@ -2283,8 +2892,8 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
spktlen = ngtcp2_conn_write_single_frame_pkt(
- conn, pi, dest, destlen, type, &conn->dcid.current.cid, ackfr,
- NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+ conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
if (spktlen <= 0) {
return spktlen;
@@ -2333,6 +2942,11 @@ static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
"discarding Initial packet number space");
conn_discard_pktns(conn, &conn->in_pktns, ts);
+
+ conn_vneg_crypto_free(conn);
+
+ memset(&conn->vneg.rx, 0, sizeof(conn->vneg.rx));
+ memset(&conn->vneg.tx, 0, sizeof(conn->vneg.tx));
}
/*
@@ -2429,15 +3043,13 @@ static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn,
static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn,
ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
- size_t early_datalen,
+ uint64_t early_datalen,
ngtcp2_tstamp ts) {
int rv;
- assert(conn->callbacks.client_initial);
-
- rv = conn->callbacks.client_initial(conn, conn->user_data);
+ rv = conn_call_client_initial(conn);
if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ return rv;
}
return conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
@@ -2446,6 +3058,39 @@ static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn,
}
/*
+ * dcid_tx_left returns the maximum number of bytes that server is
+ * allowed to send to an unvalidated path associated to |dcid|.
+ */
+static uint64_t dcid_tx_left(ngtcp2_dcid *dcid) {
+ if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) {
+ return SIZE_MAX;
+ }
+ /* From QUIC spec: Prior to validating the client address, servers
+ MUST NOT send more than three times as many bytes as the number
+ of bytes they have received. */
+ assert(dcid->bytes_recv * 3 >= dcid->bytes_sent);
+
+ return dcid->bytes_recv * 3 - dcid->bytes_sent;
+}
+
+/*
+ * conn_server_tx_left returns the maximum number of bytes that server
+ * is allowed to send to an unvalidated path.
+ */
+static uint64_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) {
+ assert(conn->server);
+
+ /* If pv->dcid has the current path, use conn->dcid.current. This
+ is because conn->dcid.current gets update for bytes_recv and
+ bytes_sent. */
+ if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) {
+ return dcid_tx_left(&conn->dcid.current);
+ }
+
+ return dcid_tx_left(dcid);
+}
+
+/*
* conn_write_handshake_pkts writes Initial and Handshake packets in
* the buffer pointed by |dest| whose length is |destlen|.
*
@@ -2460,13 +3105,13 @@ static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn,
static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn,
ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
- size_t early_datalen,
+ uint64_t write_datalen,
ngtcp2_tstamp ts) {
ngtcp2_ssize nwrite;
ngtcp2_ssize res = 0;
- int64_t prev_pkt_num = -1;
ngtcp2_rtb_entry *rtbent;
uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
ngtcp2_ksl_it it;
/* As a client, we would like to discard Initial packet number space
@@ -2491,17 +3136,9 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn,
padded. */
conn_discard_initial_state(conn, ts);
} else if (conn->in_pktns) {
- if (conn->server) {
- it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
- if (!ngtcp2_ksl_it_end(&it)) {
- rtbent = ngtcp2_ksl_it_get(&it);
- prev_pkt_num = rtbent->hd.pkt_num;
- }
- }
-
nwrite =
conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
- NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts);
+ NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts);
if (nwrite < 0) {
assert(nwrite != NGTCP2_ERR_NOBUF);
return nwrite;
@@ -2510,6 +3147,15 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn,
if (nwrite == 0) {
if (conn->server && (conn->in_pktns->rtb.probe_pkt_left ||
ngtcp2_ksl_len(&conn->in_pktns->crypto.tx.frq))) {
+ if (cstat->loss_detection_timer != UINT64_MAX &&
+ conn_server_tx_left(conn, &conn->dcid.current) <
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer canceled due to amplification limit");
+ cstat->loss_detection_timer = UINT64_MAX;
+ }
+
return 0;
}
} else {
@@ -2517,24 +3163,27 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn,
dest += nwrite;
destlen -= (size_t)nwrite;
- if (conn->server && destlen) {
- it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
- if (!ngtcp2_ksl_it_end(&it)) {
- rtbent = ngtcp2_ksl_it_get(&it);
- if (rtbent->hd.pkt_num != prev_pkt_num &&
- (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
- /* We might have already added padding to Initial, but in
- that case, we should have destlen == 0 and no Handshake
- packet will be written. */
- wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ if (destlen) {
+ /* We might have already added padding to Initial, but in that
+ case, we should have destlen == 0 and no Handshake packet
+ will be written. */
+ if (conn->server) {
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ rtbent = ngtcp2_ksl_it_get(&it);
+ if (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
}
+ } else {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
}
}
}
}
- nwrite = conn_write_handshake_pkt(conn, pi, dest, destlen,
- NGTCP2_PKT_HANDSHAKE, wflags, 0, ts);
+ nwrite = conn_write_handshake_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, wflags, write_datalen, ts);
if (nwrite < 0) {
assert(nwrite != NGTCP2_ERR_NOBUF);
return nwrite;
@@ -2608,12 +3257,13 @@ static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) {
return 0;
}
- assert(conn->remote.transport_params.active_connection_id_limit);
+ assert(conn->remote.transport_params);
+ assert(conn->remote.transport_params->active_connection_id_limit);
/* len includes retired CID. We don't provide extra CID if doing so
exceeds NGTCP2_MAX_SCID_POOL_SIZE. */
- n = conn->remote.transport_params.active_connection_id_limit +
+ n = conn->remote.transport_params->active_connection_id_limit +
conn->scid.num_retired;
return (size_t)ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len;
@@ -2667,7 +3317,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) {
return NGTCP2_ERR_NOMEM;
}
- ngtcp2_scid_init(scid, seq, &cid, token);
+ ngtcp2_scid_init(scid, seq, &cid);
rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid);
if (rv != 0) {
@@ -2675,7 +3325,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) {
return rv;
}
- rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -2718,7 +3368,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
for (; !ngtcp2_pq_empty(&conn->scid.used);) {
scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe);
- if (scid->ts_retired == UINT64_MAX || scid->ts_retired + timeout >= ts) {
+ if (scid->retired_ts == UINT64_MAX || scid->retired_ts + timeout >= ts) {
break;
}
@@ -2737,9 +3387,9 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
--conn->scid.num_retired;
}
- for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0);
- if (dcid->ts_retired + timeout >= ts) {
+ for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0);
+ if (dcid->retired_ts + timeout >= ts) {
break;
}
@@ -2748,7 +3398,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
return rv;
}
- ngtcp2_ringbuf_pop_front(&conn->dcid.retired);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb);
}
return 0;
@@ -2763,9 +3413,32 @@ static size_t conn_min_short_pktlen(ngtcp2_conn *conn) {
}
/*
+ * conn_handle_unconfirmed_key_update_from_remote deals with key
+ * update which has not been confirmed yet and initiated by the remote
+ * endpoint.
+ *
+ * If key update was initiated by the remote endpoint, acknowledging a
+ * packet encrypted with the new key completes key update procedure.
+ */
+static void conn_handle_unconfirmed_key_update_from_remote(ngtcp2_conn *conn,
+ int64_t largest_ack,
+ ngtcp2_tstamp ts) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) ||
+ (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) ||
+ largest_ack < conn->pktns.crypto.rx.ckm->pkt_num) {
+ return;
+ }
+
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+ conn->crypto.key_update.confirmed_ts = ts;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
+}
+
+/*
* conn_write_pkt writes a protected packet in the buffer pointed by
* |dest| whose length if |destlen|. |type| specifies the type of
- * packet. It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT.
+ * packet. It can be NGTCP2_PKT_1RTT or NGTCP2_PKT_0RTT.
*
* This function can send new stream data. In order to send stream
* data, specify the underlying stream and parameters to
@@ -2804,25 +3477,24 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
ngtcp2_rtb_entry *ent;
ngtcp2_strm *strm;
int pkt_empty = 1;
- size_t ndatalen = 0;
+ uint64_t ndatalen = 0;
int send_stream = 0;
int stream_blocked = 0;
int send_datagram = 0;
ngtcp2_pktns *pktns = &conn->pktns;
size_t left;
- size_t datalen = 0;
+ uint64_t datalen = 0;
ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
size_t datacnt;
- uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
+ uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
int hd_logged = 0;
ngtcp2_path_challenge_entry *pcent;
- uint8_t hd_flags;
+ uint8_t hd_flags = NGTCP2_PKT_FLAG_NONE;
int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0;
int write_more = (flags & NGTCP2_WRITE_PKT_FLAG_MORE) != 0;
int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
size_t min_pktlen = conn_min_short_pktlen(conn);
int padded = 0;
- int credit_expanded = 0;
ngtcp2_cc_pkt cc_pkt;
uint64_t crypto_offset;
uint64_t stream_offset;
@@ -2831,6 +3503,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
uint64_t target_max_data;
ngtcp2_conn_stat *cstat = &conn->cstat;
uint64_t delta;
+ const ngtcp2_cid *scid = NULL;
+ int keep_alive_expired = 0;
+ uint32_t version = 0;
/* Return 0 if destlen is less than minimum packet length which can
trigger Stateless Reset */
@@ -2861,21 +3536,22 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (!ppe_pending) {
switch (type) {
- case NGTCP2_PKT_SHORT:
- hd_flags =
- (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)
- ? NGTCP2_PKT_FLAG_KEY_PHASE
- : NGTCP2_PKT_FLAG_NONE;
+ case NGTCP2_PKT_1RTT:
+ hd_flags = conn_pkt_flags_short(conn);
+ scid = NULL;
cc->aead = pktns->crypto.ctx.aead;
cc->hp = pktns->crypto.ctx.hp;
cc->ckm = pktns->crypto.tx.ckm;
cc->hp_ctx = pktns->crypto.tx.hp_ctx;
+ assert(conn->negotiated_version);
+
+ version = conn->negotiated_version;
+
/* transport parameter is only valid after handshake completion
which means we don't know how many connection ID that remote
peer can accept before handshake completion. */
- if (conn->oscid.datalen &&
- (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ if (conn->oscid.datalen && conn_is_handshake_completed(conn)) {
rv = conn_enqueue_new_connection_id(conn);
if (rv != 0) {
return rv;
@@ -2888,11 +3564,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (!conn->early.ckm) {
return 0;
}
- hd_flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ hd_flags = conn_pkt_flags_long(conn);
+ scid = &conn->oscid;
cc->aead = conn->early.ctx.aead;
cc->hp = conn->early.ctx.hp;
cc->ckm = conn->early.ckm;
cc->hp_ctx = conn->early.hp_ctx;
+ version = conn->client_chosen_version;
break;
default:
/* Unreachable */
@@ -2903,7 +3581,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
cc->hp_mask = conn->callbacks.hp_mask;
if (conn_should_send_max_data(conn)) {
- rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -2937,12 +3615,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn->rx.max_offset = conn->rx.unsent_max_offset =
nfrc->fr.max_data.max_data;
- credit_expanded = 1;
}
- ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid,
- &conn->oscid, pktns->tx.last_pkt_num + 1,
- pktns_select_pkt_numlen(pktns), conn->version, 0);
+ ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid,
+ pktns->tx.last_pkt_num + 1,
+ pktns_select_pkt_numlen(pktns), version, 0);
ngtcp2_ppe_init(ppe, dest, destlen, cc);
@@ -2956,8 +3633,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return 0;
}
- if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) {
- pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0);
+ if (ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)) {
+ pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0);
/* PATH_RESPONSE is bound to the path that the corresponding
PATH_CHALLENGE is received. */
@@ -2970,11 +3647,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (rv != 0) {
assert(NGTCP2_ERR_NOBUF == rv);
} else {
- ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
- require_padding = !conn->server || destlen >= 1200;
+ require_padding =
+ !conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE;
/* We don't retransmit PATH_RESPONSE. */
}
}
@@ -2996,6 +3674,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
ngtcp2_acktr_commit_ack(&pktns->acktr);
ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num,
ackfr->ack.largest_ack);
+ if (type == NGTCP2_PKT_1RTT) {
+ conn_handle_unconfirmed_key_update_from_remote(
+ conn, ackfr->ack.largest_ack, ts);
+ }
pkt_empty = 0;
}
}
@@ -3006,7 +3688,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) {
frc = *pfrc;
*pfrc = (*pfrc)->next;
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
continue;
}
@@ -3017,9 +3699,22 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) {
frc = *pfrc;
*pfrc = (*pfrc)->next;
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
continue;
}
+
+ if (!(strm->flags & NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED)) {
+ strm->flags |= NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED;
+
+ rv = conn_call_stream_stop_sending(
+ conn, (*pfrc)->fr.stop_sending.stream_id,
+ (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ }
+
break;
case NGTCP2_FRAME_STREAM:
assert(0);
@@ -3029,7 +3724,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn->remote.bidi.max_streams) {
frc = *pfrc;
*pfrc = (*pfrc)->next;
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
continue;
}
break;
@@ -3038,7 +3733,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn->remote.uni.max_streams) {
frc = *pfrc;
*pfrc = (*pfrc)->next;
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
continue;
}
break;
@@ -3049,7 +3744,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
(*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) {
frc = *pfrc;
*pfrc = (*pfrc)->next;
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
continue;
}
break;
@@ -3057,13 +3752,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) {
frc = *pfrc;
*pfrc = (*pfrc)->next;
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
continue;
}
break;
- case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
- ++conn->dcid.num_retire_queued;
- break;
case NGTCP2_FRAME_CRYPTO:
assert(0);
break;
@@ -3077,6 +3769,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
pfrc = &(*pfrc)->next;
}
@@ -3117,6 +3810,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
}
}
@@ -3132,7 +3826,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
return rv;
@@ -3149,6 +3843,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
} else {
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
pfrc = &(*pfrc)->next;
}
@@ -3163,7 +3858,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
return rv;
@@ -3181,6 +3876,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
} else {
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
pfrc = &(*pfrc)->next;
}
@@ -3193,7 +3889,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
conn_should_send_max_stream_data(conn, strm)) {
- rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
return rv;
@@ -3236,8 +3932,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
pkt_empty = 0;
- credit_expanded = 1;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
pfrc = &(*pfrc)->next;
strm->rx.max_offset = strm->rx.unsent_max_offset =
@@ -3253,6 +3949,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (stream_offset == (uint64_t)-1) {
ngtcp2_strm_streamfrq_clear(strm);
ngtcp2_conn_tx_strmq_pop(conn);
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
continue;
}
@@ -3286,10 +3984,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
if (ngtcp2_strm_streamfrq_empty(strm)) {
ngtcp2_conn_tx_strmq_pop(conn);
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
continue;
}
@@ -3303,35 +4004,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
}
- /* Add ACK if MAX_DATA or MAX_STREAM_DATA frame is encoded to
- decrease packet count. */
- if (ackfr == NULL && credit_expanded) {
- rv = conn_create_ack_frame(
- conn, &ackfr, pktns, type, ts, /* ack_delay = */ 0,
- conn->local.transport_params.ack_delay_exponent);
- if (rv != 0) {
- assert(ngtcp2_err_is_fatal(rv));
- return rv;
- }
-
- if (ackfr) {
- rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr);
- if (rv != 0) {
- assert(NGTCP2_ERR_NOBUF == rv);
- } else {
- ngtcp2_acktr_commit_ack(&pktns->acktr);
- ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num,
- ackfr->ack.largest_ack);
- }
- }
- }
-
if (rv != NGTCP2_ERR_NOBUF && !send_stream && !send_datagram &&
!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
pktns->rtb.num_retransmittable && pktns->tx.frq == NULL &&
pktns->rtb.probe_pkt_left) {
- num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns,
- pktns->rtb.probe_pkt_left + 1);
+ num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1);
if (num_reclaimed < 0) {
return rv;
}
@@ -3343,9 +4020,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
those packets have been acknowledged (i.e., retransmission in
another packet). In this case, we don't have to send any
probe packet. */
- if (pktns->rtb.num_retransmittable == 0) {
+ if (pktns->rtb.num_pto_eliciting == 0) {
pktns->rtb.probe_pkt_left = 0;
ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ /* TODO If packet is empty, we should return now if cwnd is
+ zero. */
}
}
} else {
@@ -3362,11 +4041,15 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen,
left)) != (size_t)-1 &&
(ndatalen || datalen == 0)) {
- datacnt = ngtcp2_vec_copy_at_most(
- data, &ndatalen, NGTCP2_MAX_STREAM_DATACNT, vmsg->stream.data,
- vmsg->stream.datacnt, ndatalen);
+ datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT,
+ vmsg->stream.data, vmsg->stream.datacnt,
+ (size_t)ndatalen);
+ ndatalen = ngtcp2_vec_len(data, datacnt);
- rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem);
+ assert((datacnt == 0 && datalen == 0) || (datacnt && datalen));
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, datacnt, &conn->frc_objalloc, conn->mem);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
return rv;
@@ -3393,6 +4076,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
vmsg->stream.strm->tx.offset += ndatalen;
@@ -3410,16 +4094,43 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
if (rv != NGTCP2_ERR_NOBUF && send_datagram &&
- left >= ngtcp2_pkt_datagram_framelen(datalen)) {
- lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN;
- lfr.datagram.datacnt = vmsg->datagram.datacnt;
- lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data;
+ left >= ngtcp2_pkt_datagram_framelen((size_t)datalen)) {
+ if (conn->callbacks.ack_datagram || conn->callbacks.lost_datagram) {
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ nfrc->fr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN;
+ nfrc->fr.datagram.dgram_id = vmsg->datagram.dgram_id;
+ nfrc->fr.datagram.datacnt = vmsg->datagram.datacnt;
+ nfrc->fr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ assert(rv == 0);
- rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
- assert(rv == 0);
+ /* Because DATAGRAM will not be retransmitted, we do not use
+ data anymore. Just nullify it. The only reason to keep
+ track a frame is keep dgram_id to pass it to
+ ngtcp2_ack_datagram or ngtcp2_lost_datagram callbacks. */
+ nfrc->fr.datagram.datacnt = 0;
+ nfrc->fr.datagram.data = NULL;
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+ } else {
+ lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN;
+ lfr.datagram.datacnt = vmsg->datagram.datacnt;
+ lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
+ assert(rv == 0);
+ }
pkt_empty = 0;
- rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ rtb_entry_flags |=
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_DATAGRAM;
if (vmsg->datagram.paccepted) {
*vmsg->datagram.paccepted = 1;
@@ -3434,7 +4145,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return NGTCP2_ERR_STREAM_DATA_BLOCKED;
}
- if (conn->pktns.rtb.probe_pkt_left == 0) {
+ keep_alive_expired = conn_keep_alive_expired(conn, ts);
+
+ if (conn->pktns.rtb.probe_pkt_left == 0 && !keep_alive_expired) {
return 0;
}
} else if (write_more) {
@@ -3470,7 +4183,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
- if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) {
+ if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT ||
+ keep_alive_expired || conn->pktns.rtb.probe_pkt_left) {
lfr.type = NGTCP2_FRAME_PING;
rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
@@ -3480,6 +4194,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
packet is still empty. */
} else {
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ if (conn->pktns.rtb.probe_pkt_left) {
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PROBE;
+ }
pktns->tx.num_non_ack_pkt = 0;
}
} else {
@@ -3491,18 +4208,16 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
/* TODO Push STREAM frame back to ngtcp2_strm if there is an error
before ngtcp2_rtb_entry is safely created and added. */
- lfr.type = NGTCP2_FRAME_PADDING;
- if ((require_padding ||
- /* Making full sized packet will help GSO a bit */
- ngtcp2_ppe_left(ppe) < 10 ||
- (type == NGTCP2_PKT_0RTT && conn->state == NGTCP2_CS_CLIENT_INITIAL)) &&
- ngtcp2_ppe_left(ppe)) {
+ if (require_padding ||
+ /* Making full sized packet will help GSO a bit */
+ ngtcp2_ppe_left(ppe) < 10) {
lfr.padding.len = ngtcp2_ppe_padding(ppe);
} else {
lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen);
}
if (lfr.padding.len) {
+ lfr.type = NGTCP2_FRAME_PADDING;
padded = 1;
ngtcp2_log_tx_fr(&conn->log, hd, &lfr);
ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
@@ -3525,8 +4240,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts);
}
- rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite,
- rtb_entry_flags, conn->mem);
+ rv = ngtcp2_rtb_entry_objalloc_new(&ent, hd, NULL, ts, (size_t)nwrite,
+ rtb_entry_flags,
+ &conn->rtb_entry_objalloc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal((int)nwrite));
return rv;
@@ -3547,7 +4263,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
rv = conn_on_pkt_sent(conn, &pktns->rtb, ent);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_rtb_entry_del(ent, conn->mem);
+ ngtcp2_rtb_entry_objalloc_del(ent, &conn->rtb_entry_objalloc,
+ &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -3556,7 +4273,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn->cc.on_pkt_sent(
&conn->cc, &conn->cstat,
ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, (size_t)nwrite,
- NGTCP2_PKTNS_ID_APPLICATION, ts));
+ NGTCP2_PKTNS_ID_APPLICATION, ts, ent->rst.lost,
+ ent->rst.tx_in_flight, ent->rst.is_app_limited));
}
if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) {
@@ -3567,7 +4285,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts);
}
- conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING;
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_PPE_PENDING;
if (pktns->rtb.probe_pkt_left &&
(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
@@ -3577,8 +4295,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
nwrite);
}
+ conn_update_keep_alive_last_ts(conn, ts);
+
conn->dcid.current.bytes_sent += (uint64_t)nwrite;
+ conn->tx.pacing.pktlen += (size_t)nwrite;
+
ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
++pktns->tx.last_pkt_num;
@@ -3588,8 +4310,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
- uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags,
- const ngtcp2_path *path, ngtcp2_tstamp ts) {
+ uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr,
+ uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts) {
int rv;
ngtcp2_ppe ppe;
ngtcp2_pkt_hd hd;
@@ -3597,41 +4319,59 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
ngtcp2_ssize nwrite;
ngtcp2_crypto_cc cc;
ngtcp2_pktns *pktns;
- uint8_t flags;
+ uint8_t hd_flags;
ngtcp2_rtb_entry *rtbent;
int padded = 0;
+ const ngtcp2_cid *scid;
+ uint32_t version;
switch (type) {
case NGTCP2_PKT_INITIAL:
pktns = conn->in_pktns;
- flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ hd_flags = conn_pkt_flags_long(conn);
+ scid = &conn->oscid;
+ version = conn->negotiated_version ? conn->negotiated_version
+ : conn->client_chosen_version;
+ if (version == conn->client_chosen_version) {
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+ } else {
+ assert(version == conn->vneg.version);
+
+ cc.ckm = conn->vneg.tx.ckm;
+ cc.hp_ctx = conn->vneg.tx.hp_ctx;
+ }
break;
case NGTCP2_PKT_HANDSHAKE:
pktns = conn->hs_pktns;
- flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ hd_flags = conn_pkt_flags_long(conn);
+ scid = &conn->oscid;
+ version = conn->negotiated_version;
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
break;
- case NGTCP2_PKT_SHORT:
- /* 0 means Short packet. */
+ case NGTCP2_PKT_1RTT:
pktns = &conn->pktns;
- flags = (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)
- ? NGTCP2_PKT_FLAG_KEY_PHASE
- : NGTCP2_PKT_FLAG_NONE;
+ hd_flags = conn_pkt_flags_short(conn);
+ scid = NULL;
+ version = conn->negotiated_version;
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
break;
default:
/* We don't support 0-RTT packet in this function. */
assert(0);
+ abort();
}
cc.aead = pktns->crypto.ctx.aead;
cc.hp = pktns->crypto.ctx.hp;
- cc.ckm = pktns->crypto.tx.ckm;
- cc.hp_ctx = pktns->crypto.tx.hp_ctx;
cc.encrypt = conn->callbacks.encrypt;
cc.hp_mask = conn->callbacks.hp_mask;
- ngtcp2_pkt_hd_init(&hd, flags, type, dcid, &conn->oscid,
+ ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid,
pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
- conn->version, 0);
+ version, 0);
ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
@@ -3655,21 +4395,25 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
}
lfr.type = NGTCP2_FRAME_PADDING;
- switch (fr->type) {
- case NGTCP2_FRAME_PATH_CHALLENGE:
- case NGTCP2_FRAME_PATH_RESPONSE:
- if (!conn->server || destlen >= 1200) {
- lfr.padding.len = ngtcp2_ppe_padding(&ppe);
- } else {
- lfr.padding.len = 0;
- }
- break;
- default:
- if (type == NGTCP2_PKT_SHORT) {
- lfr.padding.len =
- ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn));
- } else {
- lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe);
+ if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) {
+ lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+ } else {
+ switch (fr->type) {
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+ } else {
+ lfr.padding.len = 0;
+ }
+ break;
+ default:
+ if (type == NGTCP2_PKT_1RTT) {
+ lfr.padding.len =
+ ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn));
+ } else {
+ lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe);
+ }
}
}
if (lfr.padding.len) {
@@ -3683,7 +4427,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
return nwrite;
}
- if (type == NGTCP2_PKT_SHORT) {
+ if (type == NGTCP2_PKT_1RTT) {
++cc.ckm->use_count;
}
@@ -3695,35 +4439,57 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
case NGTCP2_FRAME_ACK_ECN:
ngtcp2_acktr_commit_ack(&pktns->acktr);
ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack);
+ if (type == NGTCP2_PKT_1RTT) {
+ conn_handle_unconfirmed_key_update_from_remote(conn, fr->ack.largest_ack,
+ ts);
+ }
break;
}
- if (((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) &&
+ if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) &&
(!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) {
- if (pi) {
- conn_handle_tx_ecn(conn, pi, &rtb_flags, pktns, &hd, ts);
+ if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE)) {
+ conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts);
}
- rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags,
- conn->mem);
+ rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, (size_t)nwrite,
+ rtb_entry_flags,
+ &conn->rtb_entry_objalloc);
if (rv != 0) {
return rv;
}
rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
if (rv != 0) {
- ngtcp2_rtb_entry_del(rtbent, conn->mem);
+ ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc,
+ &conn->frc_objalloc, conn->mem);
return rv;
}
- if ((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
- (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) {
- conn_restart_timer_on_write(conn, ts);
+ if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) {
+ conn_restart_timer_on_write(conn, ts);
+ }
+
+ if (pktns->rtb.probe_pkt_left && path &&
+ ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ --pktns->rtb.probe_pkt_left;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
+ nwrite);
+ }
}
- } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+ } else if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) &&
+ conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts);
}
+ if (path && ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn_update_keep_alive_last_ts(conn, ts);
+ }
+
+ conn->tx.pacing.pktlen += (size_t)nwrite;
+
ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
++pktns->tx.last_pkt_num;
@@ -3732,7 +4498,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
}
/*
- * conn_process_early_rtb makes any pending 0RTT packet Short packet.
+ * conn_process_early_rtb makes any pending 0RTT packet 1RTT packet.
*/
static void conn_process_early_rtb(ngtcp2_conn *conn) {
ngtcp2_rtb_entry *ent;
@@ -3748,9 +4514,9 @@ static void conn_process_early_rtb(ngtcp2_conn *conn) {
continue;
}
- /* 0-RTT packet is retransmitted as a Short packet. */
+ /* 0-RTT packet is retransmitted as a 1RTT packet. */
ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM;
- ent->hd.type = NGTCP2_PKT_SHORT;
+ ent->hd.type = NGTCP2_PKT_1RTT;
}
}
@@ -3763,10 +4529,10 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) {
ngtcp2_pktns *in_pktns = conn->in_pktns;
ngtcp2_pktns *hs_pktns = conn->hs_pktns;
- return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) ||
- (in_pktns && (in_pktns->rtb.num_retransmittable ||
+ return !conn_is_handshake_completed(conn) ||
+ (in_pktns && (in_pktns->rtb.num_pto_eliciting ||
ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) ||
- (hs_pktns && (hs_pktns->rtb.num_retransmittable ||
+ (hs_pktns && (hs_pktns->rtb.num_pto_eliciting ||
ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq)));
}
@@ -3779,13 +4545,20 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) {
*
* NGTCP2_ERR_NOMEM
* Out of memory.
+ * NGTCP2_ERR_CONNECTION_ID_LIMIT
+ * The number of unacknowledged retirement exceeds the limit.
*/
static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
ngtcp2_pktns *pktns = &conn->pktns;
ngtcp2_frame_chain *nfrc;
int rv;
- rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -3809,7 +4582,7 @@ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
*/
static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
ngtcp2_tstamp ts) {
- ngtcp2_ringbuf *rb = &conn->dcid.retired;
+ ngtcp2_ringbuf *rb = &conn->dcid.retired.rb;
ngtcp2_dcid *dest, *stale_dcid;
int rv;
@@ -3827,7 +4600,7 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
dest = ngtcp2_ringbuf_push_back(rb);
ngtcp2_dcid_copy(dest, dcid);
- dest->ts_retired = ts;
+ dest->retired_ts = ts;
return conn_retire_dcid_seq(conn, dcid->seq);
}
@@ -3848,20 +4621,19 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
*/
static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
const ngtcp2_path *path, ngtcp2_tstamp ts) {
- ngtcp2_pv *pv = conn->pv;
ngtcp2_dcid *dcid, *ndcid;
ngtcp2_cid cid;
size_t i, len;
int rv;
assert(!ngtcp2_path_eq(&conn->dcid.current.ps.path, path));
- assert(!pv || !ngtcp2_path_eq(&pv->dcid.ps.path, path));
- assert(!pv || !(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
- !ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path));
+ assert(!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path));
+ assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
+ !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path));
- len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
if (ngtcp2_path_eq(&dcid->ps.path, path)) {
*pdcid = dcid;
@@ -3870,41 +4642,128 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
}
if (conn->dcid.current.cid.datalen == 0) {
- ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound);
+ ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb);
ngtcp2_cid_zero(&cid);
ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL);
- ngtcp2_path_copy(&ndcid->ps.path, path);
+ ngtcp2_dcid_set_path(ndcid, path);
*pdcid = ndcid;
return 0;
}
- if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
return NGTCP2_ERR_CONN_ID_BLOCKED;
}
- if (ngtcp2_ringbuf_full(&conn->dcid.bound)) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 0);
+ if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0);
rv = conn_retire_dcid(conn, dcid, ts);
if (rv != 0) {
return rv;
}
}
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
- ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb);
ngtcp2_dcid_copy(ndcid, dcid);
- ngtcp2_path_copy(&ndcid->ps.path, path);
+ ndcid->bound_ts = ts;
+ ngtcp2_dcid_set_path(ndcid, path);
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
*pdcid = ndcid;
return 0;
}
+static int conn_start_pmtud(ngtcp2_conn *conn) {
+ int rv;
+ size_t hard_max_udp_payload_size;
+
+ assert(!conn->local.settings.no_pmtud);
+ assert(!conn->pmtud);
+ assert(conn_is_handshake_completed(conn));
+ assert(conn->remote.transport_params);
+ assert(conn->remote.transport_params->max_udp_payload_size >=
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ hard_max_udp_payload_size =
+ (size_t)ngtcp2_min(conn->remote.transport_params->max_udp_payload_size,
+ (uint64_t)conn->local.settings.max_udp_payload_size);
+
+ rv = ngtcp2_pmtud_new(&conn->pmtud, conn->dcid.current.max_udp_payload_size,
+ hard_max_udp_payload_size,
+ conn->pktns.tx.last_pkt_num + 1, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_pmtud_finished(conn->pmtud)) {
+ ngtcp2_conn_stop_pmtud(conn);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn) {
+ return conn_start_pmtud(conn);
+}
+
+void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) {
+ if (!conn->pmtud) {
+ return;
+ }
+
+ ngtcp2_pmtud_del(conn->pmtud);
+
+ conn->pmtud = NULL;
+}
+
+static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_tstamp ts) {
+ size_t probelen;
+ ngtcp2_ssize nwrite;
+ ngtcp2_frame lfr;
+
+ assert(conn->pmtud);
+ assert(!ngtcp2_pmtud_finished(conn->pmtud));
+
+ if (!ngtcp2_pmtud_require_probe(conn->pmtud)) {
+ return 0;
+ }
+
+ probelen = ngtcp2_pmtud_probelen(conn->pmtud);
+ if (probelen > destlen) {
+ return 0;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "sending PMTUD probe packet len=%zu", probelen);
+
+ lfr.type = NGTCP2_FRAME_PING;
+
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, probelen, NGTCP2_PKT_1RTT,
+ NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, &conn->dcid.current.cid, &lfr,
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE,
+ NULL, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ assert(nwrite);
+
+ ngtcp2_pmtud_probe_sent(conn->pmtud, conn_compute_pto(conn, &conn->pktns),
+ ts);
+
+ return nwrite;
+}
+
/*
* conn_stop_pv stops the path validation which is currently running.
* This function does nothing if no path validation is currently being
@@ -3948,7 +4807,59 @@ fin:
return rv;
}
-static void conn_reset_congestion_state(ngtcp2_conn *conn);
+/*
+ * conn_abort_pv aborts the current path validation and frees
+ * resources allocated for it. This function assumes that conn->pv is
+ * not NULL.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_abort_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ int rv;
+
+ assert(pv);
+
+ if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) {
+ rv = conn_call_path_validation(conn, pv,
+ NGTCP2_PATH_VALIDATION_RESULT_ABORTED);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return conn_stop_pv(conn, ts);
+}
+
+static size_t conn_shape_udp_payload(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
+ size_t payloadlen) {
+ if (conn->remote.transport_params &&
+ conn->remote.transport_params->max_udp_payload_size) {
+ assert(conn->remote.transport_params->max_udp_payload_size >=
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ payloadlen =
+ (size_t)ngtcp2_min((uint64_t)payloadlen,
+ conn->remote.transport_params->max_udp_payload_size);
+ }
+
+ payloadlen =
+ ngtcp2_min(payloadlen, conn->local.settings.max_udp_payload_size);
+
+ if (conn->local.settings.no_udp_payload_size_shaping) {
+ return payloadlen;
+ }
+
+ return ngtcp2_min(payloadlen, dcid->max_udp_payload_size);
+}
+
+static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts);
/*
* conn_on_path_validation_failed is called when path validation
@@ -3966,10 +4877,12 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv,
ngtcp2_tstamp ts) {
int rv;
- rv = conn_call_path_validation(conn, &pv->dcid.ps.path,
- NGTCP2_PATH_VALIDATION_RESULT_FAILURE);
- if (rv != 0) {
- return rv;
+ if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) {
+ rv = conn_call_path_validation(conn, pv,
+ NGTCP2_PATH_VALIDATION_RESULT_FAILURE);
+ if (rv != 0) {
+ return rv;
+ }
}
if (pv->flags & NGTCP2_PV_FLAG_MTU_PROBE) {
@@ -3978,46 +4891,13 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv,
if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid);
- conn_reset_congestion_state(conn);
+ conn_reset_congestion_state(conn, ts);
}
return conn_stop_pv(conn, ts);
}
/*
- * dcid_tx_left returns the maximum number of bytes that server is
- * allowed to send to an unvalidated path associated to |dcid|.
- */
-static size_t dcid_tx_left(ngtcp2_dcid *dcid) {
- if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) {
- return SIZE_MAX;
- }
- /* From QUIC spec: Prior to validating the client address, servers
- MUST NOT send more than three times as many bytes as the number
- of bytes they have received. */
- assert(dcid->bytes_recv * 3 >= dcid->bytes_sent);
-
- return dcid->bytes_recv * 3 - dcid->bytes_sent;
-}
-
-/*
- * conn_server_tx_left returns the maximum number of bytes that server
- * is allowed to send to an unvalidated path.
- */
-static size_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) {
- assert(conn->server);
-
- /* If pv->dcid has the current path, use conn->dcid.current. This
- is because conn->dcid.current gets update for bytes_recv and
- bytes_sent. */
- if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) {
- return dcid_tx_left(&conn->dcid.current);
- }
-
- return dcid_tx_left(dcid);
-}
-
-/*
* conn_write_path_challenge writes a packet which includes
* PATH_CHALLENGE frame into |dest| of length |destlen|.
*
@@ -4034,19 +4914,30 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
ngtcp2_tstamp ts) {
- int rv;
ngtcp2_ssize nwrite;
ngtcp2_tstamp expiry;
ngtcp2_pv *pv = conn->pv;
ngtcp2_frame lfr;
ngtcp2_duration timeout;
uint8_t flags;
- size_t tx_left;
+ uint64_t tx_left;
+ int rv;
if (ngtcp2_pv_validation_timed_out(pv, ts)) {
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
"path validation was timed out");
- return conn_on_path_validation_failed(conn, pv, ts);
+ rv = conn_on_path_validation_failed(conn, pv, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* We might set path to the one which we just failed validate.
+ Set it to the current path here. */
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ return 0;
}
ngtcp2_pv_handle_entry_expiry(pv, ts);
@@ -4055,12 +4946,9 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
return 0;
}
- assert(conn->callbacks.rand);
- rv = conn->callbacks.rand(
- lfr.path_challenge.data, sizeof(lfr.path_challenge.data),
- &conn->local.settings.rand_ctx, NGTCP2_RAND_USAGE_PATH_CHALLENGE);
+ rv = conn_call_get_path_challenge_data(conn, lfr.path_challenge.data);
if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ return rv;
}
lfr.type = NGTCP2_FRAME_PATH_CHALLENGE;
@@ -4069,16 +4957,18 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt);
expiry = ts + timeout * (1ULL << pv->round);
+ destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
if (conn->server) {
if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
tx_left = conn_server_tx_left(conn, &pv->dcid);
- destlen = ngtcp2_min(destlen, tx_left);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left);
if (destlen == 0) {
return 0;
}
}
- if (destlen < 1200) {
+ if (destlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
flags = NGTCP2_PV_ENTRY_FLAG_UNDERSIZED;
} else {
flags = NGTCP2_PV_ENTRY_FLAG_NONE;
@@ -4090,8 +4980,10 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts);
nwrite = ngtcp2_conn_write_single_frame_pkt(
- conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr,
- NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pv->dcid.ps.path, ts);
+ conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &pv->dcid.cid, &lfr,
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING,
+ &pv->dcid.ps.path, ts);
if (nwrite <= 0) {
return nwrite;
}
@@ -4131,10 +5023,10 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
ngtcp2_frame lfr;
ngtcp2_ssize nwrite;
int rv;
- size_t tx_left;
+ uint64_t tx_left;
- for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) {
- pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0);
+ for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb);) {
+ pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0);
if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) {
/* Send PATH_RESPONSE from conn_write_pkt. */
@@ -4159,7 +5051,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
/* Client does not expect to respond to path validation against
unknown path */
- ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
pcent = NULL;
}
@@ -4181,9 +5073,11 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
}
}
+ destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
tx_left = conn_server_tx_left(conn, dcid);
- destlen = ngtcp2_min(destlen, tx_left);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left);
if (destlen == 0) {
return 0;
}
@@ -4193,8 +5087,9 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data));
nwrite = ngtcp2_conn_write_single_frame_pkt(
- conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &dcid->cid, &lfr,
- NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, ts);
+ conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &dcid->cid, &lfr, NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path,
+ ts);
if (nwrite <= 0) {
return nwrite;
}
@@ -4203,21 +5098,23 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
ngtcp2_path_copy(path, &pcent->ps.path);
}
- ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
dcid->bytes_sent += (uint64_t)nwrite;
return nwrite;
}
-ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path,
- ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_tstamp ts) {
- return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen,
- /* pdatalen = */ NULL,
- NGTCP2_WRITE_STREAM_FLAG_NONE,
- /* stream_id = */ -1,
- /* datav = */ NULL, /* datavcnt = */ 0, ts);
+ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_tstamp ts) {
+ return ngtcp2_conn_writev_stream_versioned(
+ conn, path, pkt_info_version, pi, dest, destlen,
+ /* pdatalen = */ NULL, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ /* stream_id = */ -1,
+ /* datav = */ NULL, /* datavcnt = */ 0, ts);
}
/*
@@ -4250,6 +5147,12 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn,
return NGTCP2_ERR_INVALID_ARGUMENT;
}
+ /* Version Negotiation packet is ignored if client has reacted upon
+ Version Negotiation packet. */
+ if (conn->local.settings.original_version != conn->client_chosen_version) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
if (payloadlen > sizeof(sv)) {
p = ngtcp2_mem_malloc(conn->mem, payloadlen);
if (p == NULL) {
@@ -4263,29 +5166,24 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn,
ngtcp2_log_rx_vn(&conn->log, hd, p, nsv);
- for (i = 0; i < nsv; ++i) {
- if (p[i] == conn->version) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
- "ignore Version Negotiation because it contains version "
- "selected by client");
+ ngtcp2_qlog_version_negotiation_pkt_received(&conn->qlog, hd, p, nsv);
- rv = NGTCP2_ERR_INVALID_ARGUMENT;
- goto fin;
- }
- }
+ if (!ngtcp2_is_reserved_version(conn->local.settings.original_version)) {
+ for (i = 0; i < nsv; ++i) {
+ if (p[i] == conn->local.settings.original_version) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "ignore Version Negotiation because it contains the "
+ "original version");
- if (conn->callbacks.recv_version_negotiation) {
- rv = conn->callbacks.recv_version_negotiation(conn, hd, p, nsv,
- conn->user_data);
- if (rv != 0) {
- rv = NGTCP2_ERR_CALLBACK_FAILURE;
+ rv = NGTCP2_ERR_INVALID_ARGUMENT;
+ goto fin;
+ }
}
}
- if (rv == 0) {
- /* TODO Just move to the terminal state for now in order not to
- send CONNECTION_CLOSE frame. */
- conn->state = NGTCP2_CS_DRAINING;
+ rv = conn_call_recv_version_negotiation(conn, hd, p, nsv);
+ if (rv != 0) {
+ goto fin;
}
fin:
@@ -4325,6 +5223,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
ngtcp2_stream *sfr;
ngtcp2_strm *strm;
int rv;
+ int streamfrq_empty;
if (*pfrc == NULL) {
return 0;
@@ -4341,12 +5240,13 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
if (!strm) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
break;
}
+ streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
rv = ngtcp2_strm_streamfrq_push(strm, frc);
if (rv != 0) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
if (!ngtcp2_strm_is_tx_queued(strm)) {
@@ -4356,6 +5256,9 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
return rv;
}
}
+ if (streamfrq_empty) {
+ ++conn->tx.strmq_nretrans;
+ }
break;
case NGTCP2_FRAME_CRYPTO:
frc = *pfrc;
@@ -4367,7 +5270,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
&frc->fr.crypto.offset, frc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
break;
@@ -4401,7 +5304,8 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
* ODCID does not match; or Token is empty.
*/
static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
- size_t hdpktlen, const uint8_t *pkt, size_t pktlen) {
+ size_t hdpktlen, const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
int rv;
ngtcp2_pkt_retry retry;
ngtcp2_pktns *in_pktns = conn->in_pktns;
@@ -4424,7 +5328,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
retry.odcid = conn->dcid.current.cid;
rv = ngtcp2_pkt_verify_retry_tag(
- conn->version, &retry, pkt, pktlen, conn->callbacks.encrypt,
+ conn->client_chosen_version, &retry, pkt, pktlen, conn->callbacks.encrypt,
&conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx);
if (rv != 0) {
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
@@ -4444,7 +5348,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
return 0;
}
- ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd);
+ ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd, &retry);
/* DCID must be updated before invoking callback because client
generates new initial keys there. */
@@ -4453,11 +5357,9 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY;
- assert(conn->callbacks.recv_retry);
-
- rv = conn->callbacks.recv_retry(conn, hd, conn->user_data);
+ rv = conn_call_recv_retry(conn, hd);
if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ return rv;
}
conn->state = NGTCP2_CS_CLIENT_INITIAL;
@@ -4489,7 +5391,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
ngtcp2_cpymem(token->base, retry.token.base, retry.token.len);
reset_conn_stat_recovery(&conn->cstat);
- conn_reset_congestion_state(conn);
+ conn_reset_congestion_state(conn, ts);
conn_reset_ecn_validation_state(conn);
return 0;
@@ -4497,13 +5399,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
- ngtcp2_duration pto = conn_compute_pto(conn, pktns);
- int rv = ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, pto, ts);
- if (rv != 0) {
- return rv;
- }
-
- return 0;
+ return ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, ts);
}
/*
@@ -4547,7 +5443,7 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
if (num_acked < 0) {
/* TODO assert this */
assert(ngtcp2_err_is_fatal((int)num_acked));
- ngtcp2_frame_chain_list_del(frc, conn->mem);
+ ngtcp2_frame_chain_list_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return (int)num_acked;
}
@@ -4555,11 +5451,6 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
return 0;
}
- rv = ngtcp2_conn_detect_lost_pkt(conn, pktns, &conn->cstat, ts);
- if (rv != 0) {
- return rv;
- }
-
pktns->rtb.probe_pkt_left = 0;
if (cstat->pto_count &&
@@ -4644,13 +5535,18 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn,
return 0;
}
- strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
if (strm == NULL) {
return NGTCP2_ERR_NOMEM;
}
rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
if (rv != 0) {
- ngtcp2_mem_free(conn->mem, strm);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+ return rv;
+ }
+
+ rv = conn_call_stream_open(conn, strm);
+ if (rv != 0) {
return rv;
}
}
@@ -4769,22 +5665,22 @@ static int conn_ensure_decrypt_buffer(ngtcp2_conn *conn, size_t n) {
/*
* decrypt_pkt decrypts the data pointed by |payload| whose length is
* |payloadlen|, and writes plaintext data to the buffer pointed by
- * |dest|. The buffer pointed by |ad| is the Additional Data, and its
- * length is |adlen|. |pkt_num| is used to create a nonce. |ckm| is
- * the cryptographic key, and iv to use. |decrypt| is a callback
- * function which actually decrypts a packet.
+ * |dest|. The buffer pointed by |aad| is the Additional
+ * Authenticated Data, and its length is |aadlen|. |pkt_num| is used
+ * to create a nonce. |ckm| is the cryptographic key, and iv to use.
+ * |decrypt| is a callback function which actually decrypts a packet.
*
* This function returns the number of bytes written in |dest| if it
* succeeds, or one of the following negative error codes:
*
* NGTCP2_ERR_CALLBACK_FAILURE
* User callback failed.
- * NGTCP2_ERR_TLS_DECRYPT
- * TLS backend failed to decrypt data.
+ * NGTCP2_ERR_DECRYPT
+ * Failed to decrypt a packet.
*/
static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
const uint8_t *payload, size_t payloadlen,
- const uint8_t *ad, size_t adlen,
+ const uint8_t *aad, size_t aadlen,
int64_t pkt_num, ngtcp2_crypto_km *ckm,
ngtcp2_decrypt decrypt) {
/* TODO nonce is limited to 64 bytes. */
@@ -4796,10 +5692,10 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num);
rv = decrypt(dest, aead, &ckm->aead_ctx, payload, payloadlen, nonce,
- ckm->iv.len, ad, adlen);
+ ckm->iv.len, aad, aadlen);
if (rv != 0) {
- if (rv == NGTCP2_ERR_TLS_DECRYPT) {
+ if (rv == NGTCP2_ERR_DECRYPT) {
return rv;
}
return NGTCP2_ERR_CALLBACK_FAILURE;
@@ -4825,20 +5721,17 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
* User-defined callback function failed; or it does not return
* expected result.
*/
-static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest,
- const ngtcp2_crypto_cipher *hp,
- const uint8_t *pkt, size_t pktlen,
- size_t pkt_num_offset, ngtcp2_crypto_km *ckm,
- const ngtcp2_crypto_cipher_ctx *hp_ctx,
- ngtcp2_hp_mask hp_mask) {
+static ngtcp2_ssize
+decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const uint8_t *pkt, size_t pktlen, size_t pkt_num_offset,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx, ngtcp2_hp_mask hp_mask) {
size_t sample_offset;
uint8_t *p = dest;
- uint8_t mask[NGTCP2_HP_MASKLEN];
+ uint8_t mask[NGTCP2_HP_SAMPLELEN];
size_t i;
int rv;
assert(hp_mask);
- assert(ckm);
if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) {
return NGTCP2_ERR_PROTO;
@@ -4921,40 +5814,65 @@ static int conn_emit_pending_crypto_data(ngtcp2_conn *conn,
* conn_recv_connection_close is called when CONNECTION_CLOSE or
* APPLICATION_CLOSE frame is received.
*/
-static void conn_recv_connection_close(ngtcp2_conn *conn,
- ngtcp2_connection_close *fr) {
+static int conn_recv_connection_close(ngtcp2_conn *conn,
+ ngtcp2_connection_close *fr) {
+ ngtcp2_connection_close_error *ccerr = &conn->rx.ccerr;
+
conn->state = NGTCP2_CS_DRAINING;
if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
- conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT;
+ ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT;
} else {
- conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION;
+ ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION;
+ }
+ ccerr->error_code = fr->error_code;
+ ccerr->frame_type = fr->frame_type;
+
+ if (!fr->reasonlen) {
+ ccerr->reasonlen = 0;
+
+ return 0;
}
- conn->rx.ccec.error_code = fr->error_code;
+
+ if (ccerr->reason == NULL) {
+ ccerr->reason = ngtcp2_mem_malloc(
+ conn->mem, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN);
+ if (ccerr->reason == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ }
+
+ ccerr->reasonlen =
+ ngtcp2_min(fr->reasonlen, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN);
+ ngtcp2_cpymem(ccerr->reason, fr->reason, ccerr->reasonlen);
+
+ return 0;
}
static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_path_challenge *fr) {
ngtcp2_path_challenge_entry *ent;
- /* client only responds to PATH_CHALLENGE from the current path. */
- if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
- ngtcp2_log_info(
- &conn->log, NGTCP2_LOG_EVENT_CON,
- "discard PATH_CHALLENGE from the path which is not current");
+ /* client only responds to PATH_CHALLENGE from the current path or
+ path which client is migrating to. */
+ if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
+ (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path))) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "discard PATH_CHALLENGE from the path which is not current "
+ "or endpoint is migrating to");
return;
}
- ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge);
+ ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb);
ngtcp2_path_challenge_entry_init(ent, path, fr->data);
}
/*
* conn_reset_congestion_state resets congestion state.
*/
-static void conn_reset_congestion_state(ngtcp2_conn *conn) {
+static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
conn_reset_conn_stat_cc(conn, &conn->cstat);
- conn->cc.reset(&conn->cc);
+ conn->cc.reset(&conn->cc, &conn->cstat, ts);
if (conn->hs_pktns) {
ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb,
@@ -4962,6 +5880,8 @@ static void conn_reset_congestion_state(ngtcp2_conn *conn) {
}
ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1);
ngtcp2_rst_init(&conn->rst);
+
+ conn->tx.pacing.next_ts = UINT64_MAX;
}
static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
@@ -4977,9 +5897,8 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
rv = ngtcp2_pv_validate(pv, &ent_flags, fr->data);
if (rv != 0) {
- if (rv == NGTCP2_ERR_PATH_VALIDATION_FAILED) {
- return conn_on_path_validation_failed(conn, pv, ts);
- }
+ assert(!ngtcp2_err_is_fatal(rv));
+
return 0;
}
@@ -4990,11 +5909,11 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
if (rv != 0) {
- goto fail;
+ return rv;
}
ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
}
- conn_reset_congestion_state(conn);
+ conn_reset_congestion_state(conn, ts);
conn_reset_ecn_validation_state(conn);
}
@@ -5002,10 +5921,21 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
}
- rv = conn_call_path_validation(conn, &pv->dcid.ps.path,
+ rv = conn_call_path_validation(conn, pv,
NGTCP2_PATH_VALIDATION_RESULT_SUCCESS);
if (rv != 0) {
- goto fail;
+ return rv;
+ }
+
+ if (!conn->local.settings.no_pmtud) {
+ ngtcp2_conn_stop_pmtud(conn);
+
+ if (!(pv->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
}
}
@@ -5022,7 +5952,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
NGTCP2_PV_FLAG_MTU_PROBE,
&conn->log, conn->mem);
if (rv != 0) {
- goto fail;
+ return rv;
}
npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
@@ -5032,7 +5962,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, timeout,
NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem);
if (rv != 0) {
- goto fail;
+ return rv;
}
}
@@ -5051,7 +5981,6 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
return 0;
}
-fail:
return conn_stop_pv(conn, ts);
}
@@ -5141,6 +6070,33 @@ static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns,
}
}
+/*
+ * vneg_other_versions_includes returns nonzero if |other_versions| of
+ * length |other_versionslen| includes |version|. |other_versions| is
+ * the wire image of other_versions field of version_information
+ * transport parameter, and each version is encoded in network byte
+ * order.
+ */
+static int vneg_other_versions_includes(const uint8_t *other_versions,
+ size_t other_versionslen,
+ uint32_t version) {
+ size_t i;
+
+ assert(!(other_versionslen & 0x3));
+
+ if (other_versionslen == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < other_versionslen; i += sizeof(uint32_t)) {
+ if (version == ngtcp2_get_uint32(&other_versions[i])) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
ngtcp2_strm *strm, const ngtcp2_crypto *fr);
@@ -5217,13 +6173,17 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) {
if (conn->state == NGTCP2_CS_SERVER_INITIAL) {
- /* Ignore Short packet unless server's first Handshake packet
- has been transmitted. */
+ /* Ignore 1RTT packet unless server's first Handshake packet has
+ been transmitted. */
return (ngtcp2_ssize)pktlen;
}
+ if (conn->pktns.crypto.rx.ckm) {
+ return 0;
+ }
+
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "buffering Short packet len=%zu", pktlen);
+ "buffering 1RTT packet len=%zu", pktlen);
rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen,
ts);
@@ -5239,8 +6199,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return NGTCP2_ERR_DISCARD_PKT;
}
- switch (hd.type) {
- case NGTCP2_PKT_VERSION_NEGOTIATION:
+ if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) {
hdpktlen = (size_t)nread;
ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
@@ -5276,7 +6235,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return NGTCP2_ERR_DISCARD_PKT;
}
return NGTCP2_ERR_RECV_VERSION_NEGOTIATION;
- case NGTCP2_PKT_RETRY:
+ } else if (hd.type == NGTCP2_PKT_RETRY) {
hdpktlen = (size_t)nread;
ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
@@ -5291,7 +6250,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return NGTCP2_ERR_DISCARD_PKT;
}
- rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen);
+ if (conn->client_chosen_version != hd.version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen, ts);
if (rv != 0) {
if (ngtcp2_err_is_fatal(rv)) {
return rv;
@@ -5307,7 +6270,18 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
pktlen = (size_t)nread + hd.len;
- if (conn->version != hd.version) {
+ if (!ngtcp2_is_supported_version(hd.version)) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (conn->server) {
+ if (hd.version != conn->client_chosen_version &&
+ (!conn->negotiated_version || hd.version != conn->negotiated_version)) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ } else if (hd.version != conn->client_chosen_version &&
+ conn->negotiated_version &&
+ hd.version != conn->negotiated_version) {
return NGTCP2_ERR_DISCARD_PKT;
}
@@ -5326,6 +6300,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
if (!conn->server) {
return NGTCP2_ERR_DISCARD_PKT;
}
+
+ if (hd.version != conn->client_chosen_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
if (conn->early.ckm) {
ngtcp2_ssize nread2;
@@ -5364,6 +6343,14 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
assert(conn->in_pktns);
if (conn->server) {
+ if (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PKT,
+ "Initial packet was ignored because it is included in UDP datagram "
+ "less than %zu bytes: %zu bytes",
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen);
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
if (conn->local.settings.token.len) {
rv = verify_token(&conn->local.settings.token, &hd);
if (rv != 0) {
@@ -5384,18 +6371,55 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return rv;
}
}
- } else if (hd.token.len != 0) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
- "packet was ignored because token is not empty");
- return NGTCP2_ERR_DISCARD_PKT;
+ } else {
+ if (hd.token.len != 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because token is not empty");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (hd.version != conn->client_chosen_version &&
+ !conn->negotiated_version && conn->vneg.version != hd.version) {
+ if (!vneg_other_versions_includes(conn->vneg.other_versions,
+ conn->vneg.other_versionslen,
+ hd.version)) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Install new Initial keys using QUIC version = hd.version */
+ rv = conn_call_version_negotiation(
+ conn, hd.version,
+ (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)
+ ? &conn->dcid.current.cid
+ : &conn->rcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ assert(conn->vneg.version == hd.version);
+ }
}
pktns = conn->in_pktns;
crypto = &pktns->crypto.strm;
crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL;
+ if (hd.version == conn->client_chosen_version) {
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
+ } else {
+ assert(conn->vneg.version == hd.version);
+
+ ckm = conn->vneg.rx.ckm;
+ hp_ctx = &conn->vneg.rx.hp_ctx;
+ }
+
break;
case NGTCP2_PKT_HANDSHAKE:
+ if (hd.version != conn->negotiated_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
if (!conn->hs_pktns->crypto.rx.ckm) {
if (conn->server) {
ngtcp2_log_info(
@@ -5418,6 +6442,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
pktns = conn->hs_pktns;
crypto = &pktns->crypto.strm;
crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
break;
default:
@@ -5431,8 +6457,6 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
decrypt = conn->callbacks.decrypt;
aead = &pktns->crypto.ctx.aead;
hp = &pktns->crypto.ctx.hp;
- ckm = pktns->crypto.rx.ckm;
- hp_ctx = &pktns->crypto.rx.hp_ctx;
assert(ckm);
assert(hp_mask);
@@ -5444,7 +6468,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
- (size_t)nread, ckm, hp_ctx, hp_mask);
+ (size_t)nread, hp_ctx, hp_mask);
if (nwrite < 0) {
if (ngtcp2_err_is_fatal((int)nwrite)) {
return nwrite;
@@ -5505,6 +6529,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return NGTCP2_ERR_PROTO;
}
+ if (!conn->server && hd.version != conn->client_chosen_version &&
+ !conn->negotiated_version) {
+ conn->negotiated_version = hd.version;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
+ }
+
payload = conn->crypto.decrypt_buf.base;
payloadlen = (size_t)nwrite;
@@ -5589,6 +6622,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
case NGTCP2_FRAME_PADDING:
break;
case NGTCP2_FRAME_CRYPTO:
+ if (!conn->server && !conn->negotiated_version &&
+ ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt)) {
+ conn->negotiated_version = hd.version;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
+ }
+
rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto);
if (rv != 0) {
return rv;
@@ -5596,7 +6638,10 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
require_ack = 1;
break;
case NGTCP2_FRAME_CONNECTION_CLOSE:
- conn_recv_connection_close(conn, &fr->connection_close);
+ rv = conn_recv_connection_close(conn, &fr->connection_close);
+ if (rv != 0) {
+ return rv;
+ }
break;
case NGTCP2_FRAME_PING:
require_ack = 1;
@@ -5645,21 +6690,37 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
: (ngtcp2_ssize)pktlen;
}
+static int is_unrecoverable_error(int liberr) {
+ switch (liberr) {
+ case NGTCP2_ERR_CRYPTO:
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* conn_recv_handshake_cpkt processes compound packet during
* handshake. The buffer pointed by |pkt| might contain multiple
- * packets. The Short packet must be the last one because it does not
+ * packets. The 1RTT packet must be the last one because it does not
* have payload length field.
*
* This function returns the same error code returned by
* conn_recv_handshake_pkt.
*/
-static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
- const ngtcp2_pkt_info *pi,
- const uint8_t *pkt, size_t pktlen,
- ngtcp2_tstamp ts) {
+static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
ngtcp2_ssize nread;
size_t dgramlen = pktlen;
+ const uint8_t *origpkt = pkt;
+ uint32_t version;
if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
conn->dcid.current.bytes_recv += dgramlen;
@@ -5670,53 +6731,55 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
conn_recv_handshake_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts);
if (nread < 0) {
if (ngtcp2_err_is_fatal((int)nread)) {
- return (int)nread;
+ return nread;
}
if (nread == NGTCP2_ERR_DRAINING) {
return NGTCP2_ERR_DRAINING;
}
- if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) &&
- /* Not a Version Negotiation packet */
- pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 &&
- ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) {
- if (conn->server) {
- /* If server discards first Initial, then drop connection
- state. This is because SCID in packet might be corrupted
- and the current connection state might wrongly discard
- valid packet and prevent the handshake from
- completing. */
- if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) {
- /* If this is crypto related error, then return normally
- in order to send CONNECTION_CLOSE with TLS alert (e.g.,
- no_application_protocol). */
- switch (nread) {
- case NGTCP2_ERR_CRYPTO:
- case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
- case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
- case NGTCP2_ERR_TRANSPORT_PARAM:
- return (int)nread;
+ if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && pktlen > 4) {
+ /* Not a Version Negotiation packet */
+ version = ngtcp2_get_uint32(&pkt[1]);
+ if (ngtcp2_pkt_get_type_long(version, pkt[0]) == NGTCP2_PKT_INITIAL) {
+ if (conn->server) {
+ if (is_unrecoverable_error((int)nread)) {
+ /* If server gets crypto error from TLS stack, it is
+ unrecoverable, therefore drop connection. */
+ return nread;
+ }
+
+ /* If server discards first Initial, then drop connection
+ state. This is because SCID in packet might be corrupted
+ and the current connection state might wrongly discard
+ valid packet and prevent the handshake from
+ completing. */
+ if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) {
+ return NGTCP2_ERR_DROP_CONN;
}
- return NGTCP2_ERR_DROP_CONN;
+ return (ngtcp2_ssize)dgramlen;
}
- return 0;
- }
- /* client */
- if (nread == NGTCP2_ERR_CRYPTO) {
- /* If client gets crypto error from TLS stack, it is
- unrecoverable, therefore drop connection. */
- return (int)nread;
+ /* client */
+ if (is_unrecoverable_error((int)nread)) {
+ /* If client gets crypto error from TLS stack, it is
+ unrecoverable, therefore drop connection. */
+ return nread;
+ }
+ return (ngtcp2_ssize)dgramlen;
}
- return 0;
}
if (nread == NGTCP2_ERR_DISCARD_PKT) {
- return 0;
+ return (ngtcp2_ssize)dgramlen;
}
- return (int)nread;
+ return nread;
+ }
+
+ if (nread == 0) {
+ assert(!(pkt[0] & NGTCP2_HEADER_FORM_BIT));
+ return pkt - origpkt;
}
assert(pktlen >= (size_t)nread);
@@ -5727,7 +6790,7 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
"read packet %td left %zu", nread, pktlen);
}
- return 0;
+ return (ngtcp2_ssize)dgramlen;
}
int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
@@ -5737,45 +6800,39 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
uint64_t max_tx_offset;
int local_stream = conn_local_stream(conn, stream_id);
+ assert(conn->remote.transport_params);
+
if (bidi_stream(stream_id)) {
if (local_stream) {
max_rx_offset =
conn->local.transport_params.initial_max_stream_data_bidi_local;
max_tx_offset =
- conn->remote.transport_params.initial_max_stream_data_bidi_remote;
+ conn->remote.transport_params->initial_max_stream_data_bidi_remote;
} else {
max_rx_offset =
conn->local.transport_params.initial_max_stream_data_bidi_remote;
max_tx_offset =
- conn->remote.transport_params.initial_max_stream_data_bidi_local;
+ conn->remote.transport_params->initial_max_stream_data_bidi_local;
}
} else if (local_stream) {
max_rx_offset = 0;
- max_tx_offset = conn->remote.transport_params.initial_max_stream_data_uni;
+ max_tx_offset = conn->remote.transport_params->initial_max_stream_data_uni;
} else {
max_rx_offset = conn->local.transport_params.initial_max_stream_data_uni;
max_tx_offset = 0;
}
- rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset,
- max_tx_offset, stream_user_data, conn->mem);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset,
+ max_tx_offset, stream_user_data, &conn->frc_objalloc,
+ conn->mem);
- rv = ngtcp2_map_insert(&conn->strms, &strm->me);
+ rv = ngtcp2_map_insert(&conn->strms, (ngtcp2_map_key_type)strm->stream_id,
+ strm);
if (rv != 0) {
assert(rv != NGTCP2_ERR_INVALID_ARGUMENT);
goto fail;
}
- if (!conn_local_stream(conn, stream_id)) {
- rv = conn_call_stream_open(conn, strm);
- if (rv != 0) {
- goto fail;
- }
- }
-
return 0;
fail:
@@ -5806,7 +6863,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
int rv;
uint64_t offset;
uint32_t sdflags;
- int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED;
+ int handshake_completed = conn_is_handshake_completed(conn);
if (!strm->rx.rob) {
return 0;
@@ -5816,7 +6873,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
/* Stop calling callback if application has called
ngtcp2_conn_shutdown_stream_read() inside the callback.
Because it doubly counts connection window. */
- if (strm->flags & (NGTCP2_STRM_FLAG_STOP_SENDING)) {
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
return 0;
}
@@ -5987,7 +7044,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
uint64_t rx_offset, fr_end_offset;
int local_stream;
int bidi;
- size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+ uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE;
local_stream = conn_local_stream(conn, fr->stream_id);
@@ -6038,16 +7095,22 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
return 0;
}
- strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
if (strm == NULL) {
return NGTCP2_ERR_NOMEM;
}
/* TODO Perhaps, call new_stream callback? */
rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
if (rv != 0) {
- ngtcp2_mem_free(conn->mem, strm);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
return rv;
}
+
+ rv = conn_call_stream_open(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+
if (!bidi) {
ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR);
}
@@ -6081,8 +7144,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
return NGTCP2_ERR_FINAL_SIZE;
}
- if (strm->flags &
- (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) {
+ if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) {
return 0;
}
@@ -6095,11 +7157,6 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
strm->rx.last_offset = fr_end_offset;
ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD);
-
- if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
- return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm,
- strm->app_error_code);
- }
}
} else {
if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
@@ -6113,8 +7170,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
return 0;
}
- if (strm->flags &
- (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) {
+ if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) {
return 0;
}
}
@@ -6139,6 +7195,10 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
datalen = 0;
}
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
+ }
+
fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
rx_offset == strm->rx.last_offset;
@@ -6146,11 +7206,11 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
if (fin) {
sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
}
- if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ if (!conn_is_handshake_completed(conn)) {
sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY;
}
rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
- datalen);
+ (size_t)datalen);
if (rv != 0) {
return rv;
}
@@ -6167,7 +7227,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
return rv;
}
}
- return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR);
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
}
/*
@@ -6186,7 +7246,7 @@ static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
ngtcp2_frame_chain *frc;
ngtcp2_pktns *pktns = &conn->pktns;
- rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -6219,7 +7279,7 @@ static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm,
ngtcp2_frame_chain *frc;
ngtcp2_pktns *pktns = &conn->pktns;
- rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -6367,26 +7427,31 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn,
return NGTCP2_ERR_FINAL_SIZE;
}
+ if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) {
+ return 0;
+ }
+
+ if (strm->rx.max_offset < fr->final_size) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
datalen = fr->final_size - strm->rx.last_offset;
- if (strm->rx.max_offset < fr->final_size ||
- conn_max_data_violated(conn, datalen)) {
+ if (conn_max_data_violated(conn, datalen)) {
return NGTCP2_ERR_FLOW_CONTROL;
}
- if (!(strm->flags & NGTCP2_STRM_FLAG_RECV_RST)) {
- rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size,
- fr->app_error_code, strm->stream_user_data);
- if (rv != 0) {
- return rv;
- }
+ rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size,
+ fr->app_error_code, strm->stream_user_data);
+ if (rv != 0) {
+ return rv;
+ }
- /* Extend connection flow control window for the amount of data
- which are not passed to application. */
- if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) {
- ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset -
- ngtcp2_strm_rx_offset(strm));
- }
+ /* Extend connection flow control window for the amount of data
+ which are not passed to application. */
+ if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) {
+ ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset -
+ ngtcp2_strm_rx_offset(strm));
}
conn->rx.offset += datalen;
@@ -6395,7 +7460,9 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn,
strm->rx.last_offset = fr->final_size;
strm->flags |= NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_RECV_RST;
- return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code);
+ ngtcp2_strm_set_app_error_code(strm, fr->app_error_code);
+
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
}
/*
@@ -6459,34 +7526,44 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn,
/* Frame is received reset before we create ngtcp2_strm
object. */
- strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
if (strm == NULL) {
return NGTCP2_ERR_NOMEM;
}
rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
if (rv != 0) {
- ngtcp2_mem_free(conn->mem, strm);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+ return rv;
+ }
+
+ rv = conn_call_stream_open(conn, strm);
+ if (rv != 0) {
return rv;
}
}
+ ngtcp2_strm_set_app_error_code(strm, fr->app_error_code);
+
/* No RESET_STREAM is required if we have sent FIN and all data have
been acknowledged. */
- if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) &&
- ngtcp2_strm_is_all_tx_data_acked(strm)) {
- return 0;
- }
-
- rv = conn_reset_stream(conn, strm, fr->app_error_code);
- if (rv != 0) {
- return rv;
+ if (!ngtcp2_strm_is_all_tx_data_fin_acked(strm) &&
+ !(strm->flags & NGTCP2_STRM_FLAG_SENT_RST)) {
+ rv = conn_reset_stream(conn, strm, fr->app_error_code);
+ if (rv != 0) {
+ return rv;
+ }
}
strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST;
+ if (ngtcp2_strm_is_tx_queued(strm) && !ngtcp2_strm_streamfrq_empty(strm)) {
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ }
+
ngtcp2_strm_streamfrq_clear(strm);
- return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code);
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
}
/*
@@ -6497,8 +7574,8 @@ static int check_stateless_reset(const ngtcp2_dcid *dcid,
const ngtcp2_path *path,
const ngtcp2_pkt_stateless_reset *sr) {
return ngtcp2_path_eq(&dcid->ps.path, path) &&
- ngtcp2_verify_stateless_reset_token(dcid->token,
- sr->stateless_reset_token) == 0;
+ ngtcp2_dcid_verify_stateless_reset_token(
+ dcid, sr->stateless_reset_token) == 0;
}
/*
@@ -6535,18 +7612,18 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path,
(!pv || (!check_stateless_reset(&pv->dcid, path, &sr) &&
(!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
!check_stateless_reset(&pv->fallback_dcid, path, &sr))))) {
- len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+ len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
if (check_stateless_reset(dcid, path, &sr)) {
break;
}
}
if (i == len) {
- len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
if (check_stateless_reset(dcid, path, &sr)) {
break;
}
@@ -6562,16 +7639,9 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_log_rx_sr(&conn->log, &sr);
- if (!conn->callbacks.recv_stateless_reset) {
- return 0;
- }
-
- rv = conn->callbacks.recv_stateless_reset(conn, &sr, conn->user_data);
- if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
+ ngtcp2_qlog_stateless_reset_pkt_received(&conn->qlog, &sr);
- return 0;
+ return conn_call_recv_stateless_reset(conn, &sr);
}
/*
@@ -6628,16 +7698,20 @@ static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb,
if (rv != 0) {
return rv;
}
+
if (i == 0) {
ngtcp2_ringbuf_pop_front(rb);
- } else if (i == ngtcp2_ringbuf_len(rb) - 1) {
+ continue;
+ }
+
+ if (i == ngtcp2_ringbuf_len(rb) - 1) {
ngtcp2_ringbuf_pop_back(rb);
break;
- } else {
- last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1);
- ngtcp2_dcid_copy(dcid, last);
- ngtcp2_ringbuf_pop_back(rb);
}
+
+ last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1);
+ ngtcp2_dcid_copy(dcid, last);
+ ngtcp2_ringbuf_pop_back(rb);
}
return 0;
@@ -6691,10 +7765,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
}
}
- len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
fr->stateless_reset_token);
if (rv != 0) {
@@ -6705,10 +7779,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
}
}
- len = ngtcp2_ringbuf_len(&conn->dcid.unused);
+ len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb);
for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, i);
rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
fr->stateless_reset_token);
if (rv != 0) {
@@ -6722,13 +7796,13 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
if (conn->dcid.retire_prior_to < fr->retire_prior_to) {
conn->dcid.retire_prior_to = fr->retire_prior_to;
- rv =
- conn_retire_dcid_prior_to(conn, &conn->dcid.bound, fr->retire_prior_to);
+ rv = conn_retire_dcid_prior_to(conn, &conn->dcid.bound.rb,
+ fr->retire_prior_to);
if (rv != 0) {
return rv;
}
- rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused,
+ rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb,
conn->dcid.retire_prior_to);
if (rv != 0) {
return rv;
@@ -6742,13 +7816,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
For example, a peer might send seq = 50000 and retire_prior_to
= 50000. Then send NEW_CONNECTION_ID frames with seq <
50000. */
- /* TODO we might queue lots of RETIRE_CONNECTION_ID frame here
- because conn->dcid.num_retire_queued is incremented when the
- frame is serialized. */
- if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) {
- return conn_retire_dcid_seq(conn, fr->seq);
- }
- return 0;
+ return conn_retire_dcid_seq(conn, fr->seq);
}
if (found) {
@@ -6768,7 +7836,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap);
}
- len = ngtcp2_ringbuf_len(&conn->dcid.unused);
+ len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb);
if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) {
++extra_dcid;
@@ -6795,7 +7863,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
return 0;
}
- dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused);
+ dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb);
ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token);
return 0;
@@ -6820,7 +7888,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
int rv;
if (conn->dcid.current.seq < conn->dcid.retire_prior_to) {
- if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
return 0;
}
@@ -6829,7 +7897,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
return rv;
}
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
if (pv) {
if (conn->dcid.current.seq == pv->dcid.seq) {
ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
@@ -6841,7 +7909,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
}
ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid);
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
rv = conn_call_activate_dcid(conn, &conn->dcid.current);
if (rv != 0) {
@@ -6851,13 +7919,13 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
if (pv) {
if (pv->dcid.seq < conn->dcid.retire_prior_to) {
- if (ngtcp2_ringbuf_len(&conn->dcid.unused)) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) {
rv = conn_retire_dcid(conn, &pv->dcid, ts);
if (rv != 0) {
return rv;
}
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
pv->dcid.seq == pv->fallback_dcid.seq) {
@@ -6865,7 +7933,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
}
ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
rv = conn_call_activate_dcid(conn, &pv->dcid);
if (rv != 0) {
@@ -6876,20 +7944,20 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
"path migration is aborted because connection ID is"
"retired and no unused connection ID is available");
- return conn_stop_pv(conn, ts);
+ return conn_abort_pv(conn, ts);
}
}
if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
pv->fallback_dcid.seq < conn->dcid.retire_prior_to) {
- if (ngtcp2_ringbuf_len(&conn->dcid.unused)) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) {
rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts);
if (rv != 0) {
return rv;
}
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
rv = conn_call_activate_dcid(conn, &pv->fallback_dcid);
if (rv != 0) {
@@ -6897,7 +7965,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
}
} else {
/* Now we have no fallback dcid. */
- return conn_stop_pv(conn, ts);
+ return conn_abort_pv(conn, ts);
}
}
}
@@ -6949,7 +8017,7 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn,
scid->pe.index = NGTCP2_PQ_BAD_INDEX;
}
- scid->ts_retired = ts;
+ scid->retired_ts = ts;
return ngtcp2_pq_push(&conn->scid.used, &scid->pe);
}
@@ -6970,8 +8038,6 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn,
* Server received NEW_TOKEN.
*/
static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) {
- int rv;
-
if (conn->server) {
return NGTCP2_ERR_PROTO;
}
@@ -6980,14 +8046,7 @@ static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) {
return NGTCP2_ERR_FRAME_ENCODING;
}
- if (conn->callbacks.recv_new_token) {
- rv = conn->callbacks.recv_new_token(conn, &fr->token, conn->user_data);
- if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
- }
-
- return 0;
+ return conn_call_recv_new_token(conn, &fr->token);
}
/*
@@ -7042,49 +8101,48 @@ static int conn_recv_streams_blocked_uni(ngtcp2_conn *conn,
* User-defined callback function failed.
*/
static int conn_select_preferred_addr(ngtcp2_conn *conn) {
- struct sockaddr_storage buf;
- ngtcp2_addr addr;
+ ngtcp2_path_storage ps;
int rv;
ngtcp2_duration pto, initial_pto, timeout;
ngtcp2_pv *pv;
ngtcp2_dcid *dcid;
- ngtcp2_addr_init(&addr, (struct sockaddr *)&buf, 0, NULL);
-
- if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
return 0;
}
- rv = conn_call_select_preferred_addr(conn, &addr);
+ ngtcp2_path_storage_zero(&ps);
+ ngtcp2_addr_copy(&ps.path.local, &conn->dcid.current.ps.path.local);
+
+ rv = conn_call_select_preferred_addr(conn, &ps.path);
if (rv != 0) {
return rv;
}
- if (addr.addrlen == 0 ||
- ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) {
+ if (ps.path.remote.addrlen == 0 ||
+ ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &ps.path.remote)) {
return 0;
}
assert(conn->pv == NULL);
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ngtcp2_dcid_set_path(dcid, &ps.path);
+
pto = conn_compute_pto(conn, &conn->pktns);
initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
timeout = 3 * ngtcp2_max(pto, initial_pto);
- rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
- conn->mem);
+ rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_PREFERRED_ADDR,
+ &conn->log, conn->mem);
if (rv != 0) {
/* TODO Call ngtcp2_dcid_free here if it is introduced */
return rv;
}
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
conn->pv = pv;
- ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local);
- ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr);
-
return conn_call_activate_dcid(conn, &pv->dcid);
}
@@ -7120,18 +8178,18 @@ static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
conn_discard_handshake_state(conn, ts);
- if (conn->remote.transport_params.preferred_address_present) {
+ assert(conn->remote.transport_params);
+
+ if (conn->remote.transport_params->preferred_address_present) {
rv = conn_select_preferred_addr(conn);
if (rv != 0) {
return rv;
}
}
- if (conn->callbacks.handshake_confirmed) {
- rv = conn->callbacks.handshake_confirmed(conn, conn->user_data);
- if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
+ rv = conn_call_handshake_confirmed(conn);
+ if (rv != 0) {
+ return rv;
}
/* Re-arm loss detection timer after handshake has been
@@ -7151,38 +8209,9 @@ static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
* User-defined callback function failed.
*/
static int conn_recv_datagram(ngtcp2_conn *conn, ngtcp2_datagram *fr) {
- const uint8_t *data;
- size_t datalen;
- int rv;
- uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE;
-
assert(conn->local.transport_params.max_datagram_frame_size);
- if (!conn->callbacks.recv_datagram) {
- return 0;
- }
-
- if (fr->datacnt) {
- assert(fr->datacnt == 1);
-
- data = fr->data->base;
- datalen = fr->data->len;
- } else {
- data = NULL;
- datalen = 0;
- }
-
- if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
- flags |= NGTCP2_DATAGRAM_FLAG_EARLY;
- }
-
- rv = conn->callbacks.recv_datagram(conn, flags, data, datalen,
- conn->user_data);
- if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
+ return conn_call_recv_datagram(conn, fr);
}
/*
@@ -7246,14 +8275,12 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
new_rx_ckm = conn->crypto.key_update.new_rx_ckm;
new_tx_ckm = conn->crypto.key_update.new_tx_ckm;
- assert(conn->callbacks.update_key);
-
- rv = conn->callbacks.update_key(
+ rv = conn_call_update_key(
conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, &rx_aead_ctx,
new_rx_ckm->iv.base, &tx_aead_ctx, new_tx_ckm->iv.base,
- rx_ckm->secret.base, tx_ckm->secret.base, secretlen, conn->user_data);
+ rx_ckm->secret.base, tx_ckm->secret.base, secretlen);
if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ return rv;
}
new_rx_ckm->aead_ctx = rx_aead_ctx;
@@ -7276,9 +8303,11 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
/*
* conn_rotate_keys rotates keys. The current key moves to old key,
- * and new key moves to the current key.
+ * and new key moves to the current key. If the local endpoint
+ * initiated this key update, pass nonzero as |initiator|.
*/
-static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) {
+static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num,
+ int initiator) {
ngtcp2_pktns *pktns = &conn->pktns;
assert(conn->crypto.key_update.new_rx_ckm);
@@ -7302,6 +8331,9 @@ static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) {
pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1;
conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+ if (initiator) {
+ conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR;
+ }
}
/*
@@ -7353,14 +8385,19 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
"path is migrated back to the original path");
ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid);
- conn_reset_congestion_state(conn);
+ conn_reset_congestion_state(conn, ts);
conn->dcid.current.bytes_recv += dgramlen;
conn_reset_ecn_validation_state(conn);
- rv = conn_stop_pv(conn, ts);
+
+ rv = conn_abort_pv(conn, ts);
if (rv != 0) {
return rv;
}
- return 0;
+
+ /* Run PMTUD just in case if it is prematurely aborted */
+ assert(!conn->pmtud);
+
+ return conn_start_pmtud(conn);
}
remote_addr_cmp =
@@ -7368,14 +8405,21 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
local_addr_eq =
ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local);
- /* The transport specification draft-27 says:
+ /*
+ * When to change DCID? RFC 9002 section 9.5 says:
+ *
+ * An endpoint MUST NOT reuse a connection ID when sending from more
+ * than one local address -- for example, when initiating connection
+ * migration as described in Section 9.2 or when probing a new
+ * network path as described in Section 9.1.
*
- * An endpoint MUST use a new connection ID if it initiates
- * connection migration as described in Section 9.2 or probes a new
- * network path as described in Section 9.1. An endpoint MUST use a
- * new connection ID in response to a change in the address of a
- * peer if the packet with the new peer address uses an active
- * connection ID that has not been previously used by the peer.
+ * Similarly, an endpoint MUST NOT reuse a connection ID when
+ * sending to more than one destination address. Due to network
+ * changes outside the control of its peer, an endpoint might
+ * receive packets from a new source address with the same
+ * Destination Connection ID field value, in which case it MAY
+ * continue to use the current connection ID with the new remote
+ * address while still sending from the same local address.
*/
require_new_cid = conn->dcid.current.cid.datalen &&
((new_cid_used && remote_addr_cmp) || !local_addr_eq);
@@ -7387,10 +8431,10 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
timeout = 3 * ngtcp2_max(pto, initial_pto);
- len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
for (i = 0; i < len; ++i) {
- bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+ bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) {
ngtcp2_log_info(
&conn->log, NGTCP2_LOG_EVENT_CON,
@@ -7398,13 +8442,13 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
ngtcp2_dcid_copy(&dcid, bound_dcid);
if (i == 0) {
- ngtcp2_ringbuf_pop_front(&conn->dcid.bound);
- } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) {
- ngtcp2_ringbuf_pop_back(&conn->dcid.bound);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb);
+ } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) {
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
} else {
- last = ngtcp2_ringbuf_get(&conn->dcid.bound, len - 1);
+ last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1);
ngtcp2_dcid_copy(bound_dcid, last);
- ngtcp2_ringbuf_pop_back(&conn->dcid.bound);
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
}
require_new_cid = 0;
@@ -7420,11 +8464,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
if (i == len) {
if (require_new_cid) {
- if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
return NGTCP2_ERR_CONN_ID_BLOCKED;
}
- ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused, 0));
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0));
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
rv = conn_call_activate_dcid(conn, &dcid);
if (rv != 0) {
@@ -7440,7 +8484,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
}
}
- ngtcp2_path_copy(&dcid.ps.path, path);
+ ngtcp2_dcid_set_path(&dcid, path);
dcid.bytes_recv += dgramlen;
rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE,
@@ -7452,24 +8496,35 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) {
ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid);
pv->fallback_pto = conn->pv->fallback_pto;
+ /* Unset the flag bit so that conn_stop_pv does not retire
+ DCID. */
+ conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE;
} else {
ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current);
pv->fallback_pto = pto;
}
- ngtcp2_dcid_copy(&conn->dcid.current, &dcid);
-
if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_COMPARE_FLAG_ADDR |
NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) {
- conn_reset_congestion_state(conn);
+ conn_reset_congestion_state(conn, ts);
+ } else {
+ /* For NAT rebinding, keep max_udp_payload_size since client most
+ likely does not send a padded PATH_CHALLENGE. */
+ dcid.max_udp_payload_size = ngtcp2_max(
+ dcid.max_udp_payload_size, conn->dcid.current.max_udp_payload_size);
}
+
+ ngtcp2_dcid_copy(&conn->dcid.current, &dcid);
+
conn_reset_ecn_validation_state(conn);
+ ngtcp2_conn_stop_pmtud(conn);
+
if (conn->pv) {
ngtcp2_log_info(
&conn->log, NGTCP2_LOG_EVENT_PTV,
"path migration is aborted because new migration has started");
- rv = conn_stop_pv(conn, ts);
+ rv = conn_abort_pv(conn, ts);
if (rv != 0) {
return rv;
}
@@ -7481,7 +8536,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
}
/*
- * conn_recv_pkt_from_new_path is called when a Short packet is
+ * conn_recv_pkt_from_new_path is called when a 1RTT packet is
* received from new path (not current path). This packet would be a
* packet which only contains probing frame, or reordered packet, or a
* path is being validated.
@@ -7524,7 +8579,7 @@ static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn,
return rv;
}
- ngtcp2_path_copy(&bound_dcid->ps.path, path);
+ ngtcp2_dcid_set_path(bound_dcid, path);
bound_dcid->bytes_recv += dgramlen;
return 0;
@@ -7607,7 +8662,10 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
case NGTCP2_FRAME_PADDING:
break;
case NGTCP2_FRAME_CONNECTION_CLOSE:
- conn_recv_connection_close(conn, &fr->connection_close);
+ rv = conn_recv_connection_close(conn, &fr->connection_close);
+ if (rv != 0) {
+ return rv;
+ }
break;
case NGTCP2_FRAME_CRYPTO:
case NGTCP2_FRAME_PING:
@@ -7719,7 +8777,14 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return NGTCP2_ERR_DISCARD_PKT;
}
- if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) {
+ if (pktlen < (size_t)nread + hd.len) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ assert(conn->negotiated_version);
+
+ if (hd.version != conn->client_chosen_version &&
+ hd.version != conn->negotiated_version) {
return NGTCP2_ERR_DISCARD_PKT;
}
@@ -7740,6 +8805,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
"delayed Initial packet was discarded");
return (ngtcp2_ssize)pktlen;
case NGTCP2_PKT_HANDSHAKE:
+ if (hd.version != conn->negotiated_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
if (!conn->hs_pktns) {
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
"delayed Handshake packet was discarded");
@@ -7755,7 +8824,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
decrypt = conn->callbacks.decrypt;
break;
case NGTCP2_PKT_0RTT:
- if (!conn->server) {
+ if (!conn->server || hd.version != conn->client_chosen_version) {
return NGTCP2_ERR_DISCARD_PKT;
}
@@ -7800,7 +8869,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
- (size_t)nread, ckm, hp_ctx, hp_mask);
+ (size_t)nread, hp_ctx, hp_mask);
if (nwrite < 0) {
if (ngtcp2_err_is_fatal((int)nwrite)) {
return nwrite;
@@ -7824,7 +8893,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
- if (hd.type == NGTCP2_PKT_SHORT) {
+ if (hd.type == NGTCP2_PKT_1RTT) {
key_phase_bit_changed = conn_key_phase_changed(conn, &hd);
}
@@ -7834,7 +8903,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
if (key_phase_bit_changed) {
- assert(hd.type == NGTCP2_PKT_SHORT);
+ assert(hd.type == NGTCP2_PKT_1RTT);
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE");
@@ -7867,7 +8936,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
ckm, decrypt);
if (force_decrypt_failure) {
- nwrite = NGTCP2_ERR_TLS_DECRYPT;
+ nwrite = NGTCP2_ERR_DECRYPT;
}
if (nwrite < 0) {
@@ -7875,9 +8944,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return nwrite;
}
- assert(NGTCP2_ERR_TLS_DECRYPT == nwrite);
+ assert(NGTCP2_ERR_DECRYPT == nwrite);
- if (hd.type == NGTCP2_PKT_SHORT &&
+ if (hd.type == NGTCP2_PKT_1RTT &&
++conn->crypto.decryption_failure_count >=
pktns->crypto.ctx.max_decryption_failure) {
return NGTCP2_ERR_AEAD_LIMIT_REACHED;
@@ -7963,8 +9032,6 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
"packet was ignored because of mismatched DCID");
return NGTCP2_ERR_DISCARD_PKT;
}
-
- conn->flags |= NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT;
}
ngtcp2_qlog_pkt_received_start(&conn->qlog);
@@ -7985,8 +9052,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
hd.type == NGTCP2_PKT_0RTT) {
return NGTCP2_ERR_PROTO;
}
+ assert(conn->remote.transport_params);
assign_recved_ack_delay_unscaled(
- &fr->ack, conn->remote.transport_params.ack_delay_exponent);
+ &fr->ack, conn->remote.transport_params->ack_delay_exponent);
break;
}
@@ -8092,7 +9160,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
case NGTCP2_FRAME_CONNECTION_CLOSE:
case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
- conn_recv_connection_close(conn, &fr->connection_close);
+ rv = conn_recv_connection_close(conn, &fr->connection_close);
+ if (rv != 0) {
+ return rv;
+ }
break;
case NGTCP2_FRAME_PING:
non_probing_pkt = 1;
@@ -8180,7 +9251,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
- if (conn->server && hd.type == NGTCP2_PKT_SHORT &&
+ if (conn->server && hd.type == NGTCP2_PKT_1RTT &&
!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num &&
!conn_path_validation_in_progress(conn, path)) {
@@ -8208,10 +9279,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
- if (hd.type == NGTCP2_PKT_SHORT) {
+ if (hd.type == NGTCP2_PKT_1RTT) {
if (ckm == conn->crypto.key_update.new_rx_ckm) {
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys");
- conn_rotate_keys(conn, hd.pkt_num);
+ conn_rotate_keys(conn, hd.pkt_num, /* initiator = */ 0);
} else if (ckm->pkt_num > hd.pkt_num) {
ckm->pkt_num = hd.pkt_num;
}
@@ -8220,6 +9291,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
conn->early.discard_started_ts == UINT64_MAX) {
conn->early.discard_started_ts = ts;
}
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn_update_keep_alive_last_ts(conn, ts);
+ }
}
rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts);
@@ -8250,8 +9325,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
/*
- * conn_process_buffered_protected_pkt processes buffered 0RTT or
- * Short packets.
+ * conn_process_buffered_protected_pkt processes buffered 0RTT or 1RTT
+ * packets.
*
* This function returns 0 if it succeeds, or the same negative error
* codes from conn_recv_pkt.
@@ -8331,12 +9406,55 @@ static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn,
}
static void conn_sync_stream_id_limit(ngtcp2_conn *conn) {
- ngtcp2_transport_params *params = &conn->remote.transport_params;
+ ngtcp2_transport_params *params = conn->remote.transport_params;
+
+ assert(params);
conn->local.bidi.max_streams = params->initial_max_streams_bidi;
conn->local.uni.max_streams = params->initial_max_streams_uni;
}
+static int strm_set_max_offset(void *data, void *ptr) {
+ ngtcp2_conn *conn = ptr;
+ ngtcp2_transport_params *params = conn->remote.transport_params;
+ ngtcp2_strm *strm = data;
+ uint64_t max_offset;
+ int rv;
+
+ assert(params);
+
+ if (!conn_local_stream(conn, strm->stream_id)) {
+ return 0;
+ }
+
+ if (bidi_stream(strm->stream_id)) {
+ max_offset = params->initial_max_stream_data_bidi_remote;
+ } else {
+ max_offset = params->initial_max_stream_data_uni;
+ }
+
+ if (strm->tx.max_offset < max_offset) {
+ strm->tx.max_offset = max_offset;
+
+ /* Don't call callback if stream is half-closed local */
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) {
+ return 0;
+ }
+
+ rv = conn_call_extend_max_stream_data(conn, strm, strm->stream_id,
+ strm->tx.max_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+static int conn_sync_stream_data_limit(ngtcp2_conn *conn) {
+ return ngtcp2_map_each(&conn->strms, strm_set_max_offset, conn);
+}
+
/*
* conn_handshake_completed is called once cryptographic handshake has
* completed.
@@ -8377,7 +9495,7 @@ static int conn_handshake_completed(ngtcp2_conn *conn) {
/*
* conn_recv_cpkt processes compound packet after handshake. The
- * buffer pointed by |pkt| might contain multiple packets. The Short
+ * buffer pointed by |pkt| might contain multiple packets. The 1RTT
* packet must be the last one because it does not have payload length
* field.
*
@@ -8435,11 +9553,11 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
* retired path list.
*/
static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) {
- size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+ size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
ngtcp2_dcid *dcid;
for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
if (ngtcp2_path_eq(&dcid->ps.path, path)) {
return 1;
}
@@ -8459,7 +9577,7 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
assert(conn->server);
- rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -8478,27 +9596,36 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
* reading given data. |pkt| points to the buffer to read and
* |pktlen| is the length of the buffer. |path| is the network path.
*
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes: (TBD).
+ * This function returns the number of bytes processed. Unless the
+ * last packet is 1RTT packet and an application decryption key has
+ * been installed, it returns |pktlen| if it succeeds. If it finds
+ * 1RTT packet and an application decryption key has been installed,
+ * it returns the number of bytes just before 1RTT packet begins.
+ *
+ * This function returns the number of bytes processed if it succeeds,
+ * or one of the following negative error codes: (TBD).
*/
-static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
- const ngtcp2_pkt_info *pi, const uint8_t *pkt,
- size_t pktlen, ngtcp2_tstamp ts) {
+static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
int rv;
+ ngtcp2_ssize nread;
switch (conn->state) {
case NGTCP2_CS_CLIENT_INITIAL:
/* TODO Better to log something when we ignore input */
- return 0;
+ return (ngtcp2_ssize)pktlen;
case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
- rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
- if (rv < 0) {
- return rv;
+ nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return nread;
}
if (conn->state == NGTCP2_CS_CLIENT_INITIAL) {
/* Retry packet was received */
- return 0;
+ return (ngtcp2_ssize)pktlen;
}
assert(conn->hs_pktns);
@@ -8510,20 +9637,24 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
- if ((conn->flags & (NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED |
- NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) ==
- NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) {
+ if (conn_is_handshake_completed(conn) &&
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
rv = conn_handshake_completed(conn);
if (rv != 0) {
return rv;
}
+
+ rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
}
- return 0;
+ return nread;
case NGTCP2_CS_SERVER_INITIAL:
- rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
- if (rv < 0) {
- return rv;
+ nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return nread;
}
/*
@@ -8538,7 +9669,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob)) {
/* Address has been validated with token */
if (conn->local.settings.token.len) {
- return 0;
+ return nread;
}
return NGTCP2_ERR_RETRY;
}
@@ -8562,11 +9693,11 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
- return 0;
+ return nread;
case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
- rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
- if (rv < 0) {
- return rv;
+ nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return nread;
}
if (conn->hs_pktns->crypto.rx.ckm) {
@@ -8580,7 +9711,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
conn_discard_initial_state(conn, ts);
}
- if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ if (!conn_is_handshake_completed(conn)) {
/* If server hits amplification limit, it cancels loss detection
timer. If server receives a packet from client, the limit is
increased and server can send more. If server has
@@ -8603,7 +9734,24 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
- return 0;
+ if ((size_t)nread < pktlen) {
+ /* We have 1RTT packet and application rx key, but the
+ handshake has not completed yet. */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 1RTT packet len=%zu",
+ pktlen - (size_t)nread);
+
+ rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread,
+ pktlen - (size_t)nread, pktlen, ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ return nread;
}
if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) {
@@ -8633,26 +9781,38 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
return rv;
}
+ if (!conn->local.settings.no_pmtud) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
conn->pktns.rtb.persistent_congestion_start_ts = ts;
/* Re-arm loss detection timer here after handshake has been
confirmed. */
ngtcp2_conn_set_loss_detection_timer(conn, ts);
- return 0;
+ return nread;
case NGTCP2_CS_CLOSING:
return NGTCP2_ERR_CLOSING;
case NGTCP2_CS_DRAINING:
return NGTCP2_ERR_DRAINING;
default:
- return 0;
+ return (ngtcp2_ssize)pktlen;
}
}
-int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
- const ngtcp2_pkt_info *pi, const uint8_t *pkt,
- size_t pktlen, ngtcp2_tstamp ts) {
+int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
+ int pkt_info_version,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
int rv = 0;
+ ngtcp2_ssize nread = 0;
+ const ngtcp2_pkt_info zero_pi = {0};
+ (void)pkt_info_version;
conn->log.last_ts = ts;
conn->qlog.last_ts = ts;
@@ -8673,11 +9833,29 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return 0;
}
+ if (!pi) {
+ pi = &zero_pi;
+ }
+
switch (conn->state) {
case NGTCP2_CS_CLIENT_INITIAL:
case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED:
- return conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
+ nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ if ((size_t)nread == pktlen) {
+ return 0;
+ }
+
+ assert(conn->pktns.crypto.rx.ckm);
+
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ break;
case NGTCP2_CS_SERVER_INITIAL:
case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED:
@@ -8694,7 +9872,22 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return 0;
}
- return conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
+
+ nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ if ((size_t)nread == pktlen) {
+ return 0;
+ }
+
+ assert(conn->pktns.crypto.rx.ckm);
+
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ break;
case NGTCP2_CS_CLOSING:
return NGTCP2_ERR_CLOSING;
case NGTCP2_CS_DRAINING:
@@ -8704,11 +9897,13 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
if (rv != 0) {
return rv;
}
- return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts);
+ break;
default:
assert(0);
abort();
}
+
+ return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts);
}
/*
@@ -8731,9 +9926,10 @@ static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) {
static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn,
ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
+ uint8_t flags,
ngtcp2_tstamp ts) {
- return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT,
- NGTCP2_WRITE_PKT_FLAG_NONE, ts);
+ return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, flags,
+ ts);
}
/*
@@ -8747,10 +9943,44 @@ static int conn_handshake_probe_left(ngtcp2_conn *conn) {
}
/*
+ * conn_validate_early_transport_params_limits validates that the
+ * limits in transport parameters remembered by client for early data
+ * are not reduced. This function is only used by client and should
+ * only be called when early data is accepted by server.
+ */
+static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) {
+ const ngtcp2_transport_params *params = conn->remote.transport_params;
+
+ assert(!conn->server);
+ assert(params);
+
+ if (conn->early.transport_params.active_connection_id_limit >
+ params->active_connection_id_limit ||
+ conn->early.transport_params.initial_max_data >
+ params->initial_max_data ||
+ conn->early.transport_params.initial_max_stream_data_bidi_local >
+ params->initial_max_stream_data_bidi_local ||
+ conn->early.transport_params.initial_max_stream_data_bidi_remote >
+ params->initial_max_stream_data_bidi_remote ||
+ conn->early.transport_params.initial_max_stream_data_uni >
+ params->initial_max_stream_data_uni ||
+ conn->early.transport_params.initial_max_streams_bidi >
+ params->initial_max_streams_bidi ||
+ conn->early.transport_params.initial_max_streams_uni >
+ params->initial_max_streams_uni ||
+ conn->early.transport_params.max_datagram_frame_size >
+ params->max_datagram_frame_size) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ return 0;
+}
+
+/*
* conn_write_handshake writes QUIC handshake packets to the buffer
- * pointed by |dest| of length |destlen|. |early_datalen| specifies
- * the expected length of early data to send. Specify 0 to
- * |early_datalen| if there is no early data.
+ * pointed by |dest| of length |destlen|. |write_datalen| specifies
+ * the expected length of 0RTT or 1RTT packet payload. Specify 0 to
+ * |write_datalen| if there is no such data.
*
* This function returns the number of bytes written to the buffer, or
* one of the following negative error codes:
@@ -8771,14 +10001,12 @@ static int conn_handshake_probe_left(ngtcp2_conn *conn) {
*/
static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
- size_t early_datalen,
+ uint64_t write_datalen,
ngtcp2_tstamp ts) {
int rv;
ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0;
size_t origlen = destlen;
- size_t server_tx_left;
- ngtcp2_conn_stat *cstat = &conn->cstat;
- size_t pending_early_datalen;
+ uint64_t pending_early_datalen;
ngtcp2_dcid *dcid;
ngtcp2_preferred_addr *paddr;
@@ -8786,27 +10014,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
case NGTCP2_CS_CLIENT_INITIAL:
pending_early_datalen = conn_retry_early_payloadlen(conn);
if (pending_early_datalen) {
- early_datalen = pending_early_datalen;
+ write_datalen = pending_early_datalen;
}
if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) {
nwrite =
- conn_write_client_initial(conn, pi, dest, destlen, early_datalen, ts);
+ conn_write_client_initial(conn, pi, dest, destlen, write_datalen, ts);
if (nwrite <= 0) {
return nwrite;
}
} else {
nwrite = conn_write_handshake_pkt(
conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
- NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts);
+ NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts);
if (nwrite < 0) {
return nwrite;
}
}
if (pending_early_datalen) {
- early_spktlen = conn_retransmit_retry_early(conn, pi, dest + nwrite,
- destlen - (size_t)nwrite, ts);
+ early_spktlen = conn_retransmit_retry_early(
+ conn, pi, dest + nwrite, destlen - (size_t)nwrite,
+ nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING
+ : NGTCP2_WRITE_PKT_FLAG_NONE,
+ ts);
if (early_spktlen < 0) {
assert(ngtcp2_err_is_fatal((int)early_spktlen));
@@ -8826,12 +10057,12 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
pending_early_datalen = conn_retry_early_payloadlen(conn);
if (pending_early_datalen) {
- early_datalen = pending_early_datalen;
+ write_datalen = pending_early_datalen;
}
}
nwrite =
- conn_write_handshake_pkts(conn, pi, dest, destlen, early_datalen, ts);
+ conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -8841,9 +10072,10 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
destlen -= (size_t)nwrite;
}
- if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ if (!conn_is_handshake_completed(conn)) {
if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
- nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, ts);
+ nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen,
+ NGTCP2_WRITE_PKT_FLAG_NONE, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -8866,13 +10098,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
}
+ if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED) &&
+ !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
+ rv = conn_validate_early_transport_params_limits(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ /* Server might increase stream data limits. Extend it if we have
+ streams created for early data. */
+ rv = conn_sync_stream_data_limit(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
conn->state = NGTCP2_CS_POST_HANDSHAKE;
- if (conn->remote.transport_params.preferred_address_present) {
- assert(!ngtcp2_ringbuf_full(&conn->dcid.unused));
+ assert(conn->remote.transport_params);
- paddr = &conn->remote.transport_params.preferred_address;
- dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused);
+ if (conn->remote.transport_params->preferred_address_present) {
+ assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb));
+
+ paddr = &conn->remote.transport_params->preferred_address;
+ dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb);
ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token);
rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1);
@@ -8881,11 +10130,12 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
}
- if (conn->remote.transport_params.stateless_reset_token_present) {
+ if (conn->remote.transport_params->stateless_reset_token_present) {
assert(conn->dcid.current.seq == 0);
- memcpy(conn->dcid.current.token,
- conn->remote.transport_params.stateless_reset_token,
- sizeof(conn->dcid.current.token));
+ assert(!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT));
+ ngtcp2_dcid_set_token(
+ &conn->dcid.current,
+ conn->remote.transport_params->stateless_reset_token);
}
rv = conn_call_activate_dcid(conn, &conn->dcid.current);
@@ -8895,15 +10145,17 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn_process_early_rtb(conn);
- rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts);
- if (rv != 0) {
- return (ngtcp2_ssize)rv;
+ if (!conn->local.settings.no_pmtud) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
}
return res;
case NGTCP2_CS_SERVER_INITIAL:
- nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen,
- /* early_datalen = */ 0, ts);
+ nwrite =
+ conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -8914,47 +10166,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return nwrite;
case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
- if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
- if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
- server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
- if (server_tx_left == 0) {
- if (cstat->loss_detection_timer != UINT64_MAX) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
- "loss detection timer canceled");
- cstat->loss_detection_timer = UINT64_MAX;
- cstat->pto_count = 0;
- }
- return 0;
- }
- }
-
- if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) {
- nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen,
- /* early_datalen = */ 0, ts);
- if (nwrite < 0) {
- return nwrite;
- }
-
- res += nwrite;
- dest += nwrite;
- destlen -= (size_t)nwrite;
+ if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) {
+ nwrite =
+ conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts);
+ if (nwrite < 0) {
+ return nwrite;
}
- if (res == 0) {
- nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
- if (nwrite < 0) {
- return nwrite;
- }
+ res += nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
- res += nwrite;
- dest += nwrite;
- origlen -= (size_t)nwrite;
+ if (res == 0) {
+ nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ if (nwrite < 0) {
+ return nwrite;
}
- return res;
+ res += nwrite;
+ dest += nwrite;
+ origlen -= (size_t)nwrite;
}
- return 0;
+ return res;
case NGTCP2_CS_CLOSING:
return NGTCP2_ERR_CLOSING;
case NGTCP2_CS_DRAINING:
@@ -8996,17 +10231,17 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
int send_stream = 0;
int send_datagram = 0;
ngtcp2_ssize spktlen, early_spktlen;
- int was_client_initial;
- size_t datalen;
- size_t early_datalen = 0;
+ uint64_t datalen;
+ uint64_t write_datalen = 0;
uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+ uint32_t version;
assert(!conn->server);
/* conn->early.ckm might be created in the first call of
conn_handshake(). Check it later. */
- if (vmsg && !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
+ if (vmsg) {
switch (vmsg->type) {
case NGTCP2_VMSG_TYPE_STREAM:
datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt);
@@ -9018,9 +10253,11 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
(vmsg->stream.strm->tx.max_offset - vmsg->stream.strm->tx.offset) &&
(conn->tx.max_offset - conn->tx.offset)));
if (send_stream) {
- early_datalen =
- conn_enforce_flow_control(conn, vmsg->stream.strm, datalen) +
- NGTCP2_STREAM_OVERHEAD;
+ write_datalen =
+ conn_enforce_flow_control(conn, vmsg->stream.strm, datalen);
+ write_datalen =
+ ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ write_datalen += NGTCP2_STREAM_OVERHEAD;
if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) {
wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
@@ -9031,11 +10268,9 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
break;
case NGTCP2_VMSG_TYPE_DATAGRAM:
datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt);
- /* TODO Do we need this? DATAGRAM is independent from STREAM
- data and no retransmission */
send_datagram = conn_retry_early_payloadlen(conn) == 0;
if (send_datagram) {
- early_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD;
+ write_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD;
if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) {
wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
@@ -9048,32 +10283,39 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
}
if (!ppe_pending) {
- was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL;
- spktlen = conn_write_handshake(conn, pi, dest, destlen, early_datalen, ts);
+ spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts);
if (spktlen < 0) {
return spktlen;
}
- if (conn->pktns.crypto.tx.ckm || !conn->early.ckm ||
- (!send_stream && !send_datagram)) {
+ if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) ||
+ !conn->early.ckm || (!send_stream && !send_datagram)) {
return spktlen;
}
+
+ /* If spktlen > 0, we are making a compound packet. If Initial
+ packet is written, we have to pad bytes to 0-RTT packet. */
+ version = conn->negotiated_version ? conn->negotiated_version
+ : conn->client_chosen_version;
+ if (spktlen > 0 &&
+ ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ conn->pkt.require_padding = 1;
+ } else {
+ conn->pkt.require_padding = 0;
+ }
} else {
+ assert(!conn->pktns.crypto.rx.ckm);
assert(!conn->pktns.crypto.tx.ckm);
assert(conn->early.ckm);
- was_client_initial = conn->pkt.was_client_initial;
+ if (conn->pkt.require_padding) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
spktlen = conn->pkt.hs_spktlen;
}
- /* If spktlen > 0, we are making a compound packet. If Initial
- packet is written, we have to pad bytes to 0-RTT packet. */
-
- if (spktlen && was_client_initial) {
- wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
- }
-
dest += spktlen;
destlen -= (size_t)spktlen;
@@ -9089,7 +10331,6 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
return spktlen;
case NGTCP2_ERR_WRITE_MORE:
- conn->pkt.was_client_initial = was_client_initial;
conn->pkt.hs_spktlen = spktlen;
break;
}
@@ -9107,7 +10348,7 @@ void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) {
}
int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) {
- return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+ return conn_is_handshake_completed(conn) &&
(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED);
}
@@ -9136,35 +10377,29 @@ int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) {
}
if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) {
- return -1;
+ return NGTCP2_ERR_INVALID_ARGUMENT;
}
nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen);
if (nread < 0) {
- return -1;
+ return (int)nread;
}
switch (p->type) {
case NGTCP2_PKT_INITIAL:
- if (pktlen < NGTCP2_MIN_INITIAL_PKTLEN) {
- return -1;
- }
- if (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
- return -1;
- }
break;
case NGTCP2_PKT_0RTT:
/* 0-RTT packet may arrive before Initial packet due to
- re-ordering. */
- break;
+ re-ordering. ngtcp2 does not buffer 0RTT packet unless the
+ very first Initial packet is received or token is received. */
+ return NGTCP2_ERR_RETRY;
default:
- return -1;
+ return NGTCP2_ERR_INVALID_ARGUMENT;
}
- if (p->version != NGTCP2_PROTO_VER_V1 &&
- (p->version < NGTCP2_PROTO_VER_DRAFT_MIN ||
- NGTCP2_PROTO_VER_DRAFT_MAX < p->version)) {
- return 1;
+ if (pktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE ||
+ (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
}
return 0;
@@ -9178,6 +10413,7 @@ int ngtcp2_conn_install_initial_key(
ngtcp2_pktns *pktns = conn->in_pktns;
int rv;
+ assert(ivlen >= 8);
assert(pktns);
conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx);
@@ -9220,12 +10456,64 @@ int ngtcp2_conn_install_initial_key(
return 0;
}
+int ngtcp2_conn_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv,
+ const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+ const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+ const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) {
+ int rv;
+
+ assert(ivlen >= 8);
+
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx);
+ conn->vneg.rx.hp_ctx.native_handle = NULL;
+
+ if (conn->vneg.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem);
+ conn->vneg.rx.ckm = NULL;
+ }
+
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx);
+ conn->vneg.tx.hp_ctx.native_handle = NULL;
+
+ if (conn->vneg.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem);
+ conn->vneg.tx.ckm = NULL;
+ }
+
+ rv = ngtcp2_crypto_km_new(&conn->vneg.rx.ckm, NULL, 0, NULL, rx_iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* Take owner ship after we are sure that no failure occurs, so that
+ caller can delete these contexts on failure. */
+ conn->vneg.rx.ckm->aead_ctx = *rx_aead_ctx;
+ conn->vneg.rx.hp_ctx = *rx_hp_ctx;
+ conn->vneg.tx.ckm->aead_ctx = *tx_aead_ctx;
+ conn->vneg.tx.hp_ctx = *tx_hp_ctx;
+ conn->vneg.version = version;
+
+ return 0;
+}
+
int ngtcp2_conn_install_rx_handshake_key(
ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx,
const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) {
ngtcp2_pktns *pktns = conn->hs_pktns;
int rv;
+ assert(ivlen >= 8);
assert(pktns);
assert(!pktns->crypto.rx.hp_ctx.native_handle);
assert(!pktns->crypto.rx.ckm);
@@ -9238,6 +10526,16 @@ int ngtcp2_conn_install_rx_handshake_key(
pktns->crypto.rx.hp_ctx = *hp_ctx;
+ rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
+ pktns->crypto.rx.ckm = NULL;
+
+ memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx));
+
+ return rv;
+ }
+
return 0;
}
@@ -9247,6 +10545,7 @@ int ngtcp2_conn_install_tx_handshake_key(
ngtcp2_pktns *pktns = conn->hs_pktns;
int rv;
+ assert(ivlen >= 8);
assert(pktns);
assert(!pktns->crypto.tx.hp_ctx.native_handle);
assert(!pktns->crypto.tx.ckm);
@@ -9260,7 +10559,20 @@ int ngtcp2_conn_install_tx_handshake_key(
pktns->crypto.tx.hp_ctx = *hp_ctx;
if (conn->server) {
- return ngtcp2_conn_commit_local_transport_params(conn);
+ rv = ngtcp2_conn_commit_local_transport_params(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+ pktns->crypto.tx.ckm = NULL;
+
+ memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx));
+
+ return rv;
}
return 0;
@@ -9272,6 +10584,7 @@ int ngtcp2_conn_install_early_key(ngtcp2_conn *conn,
const ngtcp2_crypto_cipher_ctx *hp_ctx) {
int rv;
+ assert(ivlen >= 8);
assert(!conn->early.hp_ctx.native_handle);
assert(!conn->early.ckm);
@@ -9283,6 +10596,22 @@ int ngtcp2_conn_install_early_key(ngtcp2_conn *conn,
conn->early.hp_ctx = *hp_ctx;
+ conn->flags |= NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED;
+
+ if (conn->server) {
+ rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY);
+ } else {
+ rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY);
+ }
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
+ conn->early.ckm = NULL;
+
+ memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx));
+
+ return rv;
+ }
+
return 0;
}
@@ -9294,6 +10623,7 @@ int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret,
ngtcp2_pktns *pktns = &conn->pktns;
int rv;
+ assert(ivlen >= 8);
assert(!pktns->crypto.rx.hp_ctx.native_handle);
assert(!pktns->crypto.rx.ckm);
@@ -9306,9 +10636,28 @@ int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret,
pktns->crypto.rx.hp_ctx = *hp_ctx;
if (!conn->server) {
- conn->remote.transport_params = conn->remote.pending_transport_params;
- conn_sync_stream_id_limit(conn);
- conn->tx.max_offset = conn->remote.transport_params.initial_max_data;
+ if (conn->remote.pending_transport_params) {
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+
+ conn->remote.transport_params = conn->remote.pending_transport_params;
+ conn->remote.pending_transport_params = NULL;
+ conn_sync_stream_id_limit(conn);
+ conn->tx.max_offset = conn->remote.transport_params->initial_max_data;
+ }
+
+ if (conn->early.ckm) {
+ conn_discard_early_key(conn);
+ }
+ }
+
+ rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
+ pktns->crypto.rx.ckm = NULL;
+
+ memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx));
+
+ return rv;
}
return 0;
@@ -9322,6 +10671,7 @@ int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret,
ngtcp2_pktns *pktns = &conn->pktns;
int rv;
+ assert(ivlen >= 8);
assert(!pktns->crypto.tx.hp_ctx.native_handle);
assert(!pktns->crypto.tx.ckm);
@@ -9334,13 +10684,28 @@ int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret,
pktns->crypto.tx.hp_ctx = *hp_ctx;
if (conn->server) {
- conn->remote.transport_params = conn->remote.pending_transport_params;
- conn_sync_stream_id_limit(conn);
- conn->tx.max_offset = conn->remote.transport_params.initial_max_data;
+ if (conn->remote.pending_transport_params) {
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+
+ conn->remote.transport_params = conn->remote.pending_transport_params;
+ conn->remote.pending_transport_params = NULL;
+ conn_sync_stream_id_limit(conn);
+ conn->tx.max_offset = conn->remote.transport_params->initial_max_data;
+ }
} else if (conn->early.ckm) {
conn_discard_early_key(conn);
}
+ rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+ pktns->crypto.tx.ckm = NULL;
+
+ memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx));
+
+ return rv;
+ }
+
return 0;
}
@@ -9358,7 +10723,59 @@ int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
return NGTCP2_ERR_INVALID_STATE;
}
- conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM);
+ conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM, /* initiator = */ 1);
+
+ return 0;
+}
+
+/*
+ * conn_retire_stale_bound_dcid retires stale destination connection
+ * ID in conn->dcid.bound to keep some unused destination connection
+ * IDs available.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn,
+ ngtcp2_duration timeout,
+ ngtcp2_tstamp ts) {
+ size_t i;
+ ngtcp2_dcid *dcid, *last;
+ int rv;
+
+ for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+
+ assert(dcid->cid.datalen);
+
+ if (dcid->bound_ts + timeout > ts) {
+ ++i;
+ continue;
+ }
+
+ rv = conn_retire_dcid_seq(conn, dcid->seq);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (i == 0) {
+ ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb);
+ continue;
+ }
+
+ if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) {
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
+ break;
+ }
+
+ last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb,
+ ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1);
+ ngtcp2_dcid_copy(dcid, last);
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
+ }
return 0;
}
@@ -9372,21 +10789,41 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) {
ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
ngtcp2_scid *scid;
ngtcp2_dcid *dcid;
+ size_t i, len;
if (conn->pv) {
res = ngtcp2_pv_next_expiry(conn->pv);
}
+ if (conn->pmtud) {
+ res = ngtcp2_min(res, conn->pmtud->expiry);
+ }
+
if (!ngtcp2_pq_empty(&conn->scid.used)) {
scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe);
- if (scid->ts_retired != UINT64_MAX) {
- res = ngtcp2_min(res, scid->ts_retired + pto);
+ if (scid->retired_ts != UINT64_MAX) {
+ t = scid->retired_ts + pto;
+ res = ngtcp2_min(res, t);
}
}
- if (ngtcp2_ringbuf_len(&conn->dcid.retired)) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0);
- res = ngtcp2_min(res, dcid->ts_retired + pto);
+ if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0);
+ t = dcid->retired_ts + pto;
+ res = ngtcp2_min(res, t);
+ }
+
+ if (conn->dcid.current.cid.datalen) {
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+
+ assert(dcid->cid.datalen);
+ assert(dcid->bound_ts != UINT64_MAX);
+
+ t = dcid->bound_ts + 3 * pto;
+ res = ngtcp2_min(res, t);
+ }
}
if (conn->server && conn->early.ckm &&
@@ -9408,28 +10845,60 @@ ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) {
return UINT64_MAX;
}
+static ngtcp2_tstamp conn_handshake_expiry(ngtcp2_conn *conn) {
+ if (conn_is_handshake_completed(conn) ||
+ conn->local.settings.handshake_timeout == UINT64_MAX) {
+ return UINT64_MAX;
+ }
+
+ return conn->local.settings.initial_ts +
+ conn->local.settings.handshake_timeout;
+}
+
ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) {
ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn);
ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn);
ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn);
ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn);
+ ngtcp2_tstamp t5 = conn_keep_alive_expiry(conn);
+ ngtcp2_tstamp t6 = conn_handshake_expiry(conn);
+ ngtcp2_tstamp t7 = ngtcp2_conn_get_idle_expiry(conn);
ngtcp2_tstamp res = ngtcp2_min(t1, t2);
res = ngtcp2_min(res, t3);
- return ngtcp2_min(res, t4);
+ res = ngtcp2_min(res, t4);
+ res = ngtcp2_min(res, t5);
+ res = ngtcp2_min(res, t6);
+ res = ngtcp2_min(res, t7);
+ return ngtcp2_min(res, conn->tx.pacing.next_ts);
}
int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
int rv;
ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+ if (ngtcp2_conn_get_idle_expiry(conn) <= ts) {
+ return NGTCP2_ERR_IDLE_CLOSE;
+ }
+
ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts);
+ conn_cancel_expired_keep_alive_timer(conn, ts);
+
+ conn_cancel_expired_pkt_tx_timer(conn, ts);
+
ngtcp2_conn_remove_lost_pkt(conn, ts);
if (conn->pv) {
ngtcp2_pv_cancel_expired_timer(conn->pv, ts);
}
+ if (conn->pmtud) {
+ ngtcp2_pmtud_handle_expiry(conn->pmtud, ts);
+ if (ngtcp2_pmtud_finished(conn->pmtud)) {
+ ngtcp2_conn_stop_pmtud(conn);
+ }
+ }
+
if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) {
rv = ngtcp2_conn_on_loss_detection_timer(conn, ts);
if (rv != 0) {
@@ -9437,6 +10906,13 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
}
}
+ if (conn->dcid.current.cid.datalen) {
+ rv = conn_retire_stale_bound_dcid(conn, 3 * pto, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
rv = conn_remove_retired_connection_id(conn, pto, ts);
if (rv != 0) {
return rv;
@@ -9449,26 +10925,38 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
}
}
+ if (!conn_is_handshake_completed(conn) &&
+ conn->local.settings.handshake_timeout != UINT64_MAX &&
+ conn->local.settings.initial_ts +
+ conn->local.settings.handshake_timeout <=
+ ts) {
+ return NGTCP2_ERR_HANDSHAKE_TIMEOUT;
+ }
+
return 0;
}
static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr,
+ ngtcp2_duration max_ack_delay,
ngtcp2_tstamp ts) {
if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
- acktr->first_unacked_ts <= ts) {
+ acktr->first_unacked_ts != UINT64_MAX &&
+ acktr->first_unacked_ts + max_ack_delay <= ts) {
acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER;
}
}
void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
ngtcp2_tstamp ts) {
+ ngtcp2_duration ack_delay = conn_compute_ack_delay(conn);
+
if (conn->in_pktns) {
- acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts);
+ acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, 0, ts);
}
if (conn->hs_pktns) {
- acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts);
+ acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, 0, ts);
}
- acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts);
+ acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ack_delay, ts);
}
ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) {
@@ -9515,18 +11003,53 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
}
/*
+ * select_preferred_version selects the most preferred version.
+ * |fallback_version| is chosen if no preference is made, or
+ * |preferred_versions| does not include any of |chosen_version| or
+ * |other_versions|. |chosen_version| is treated as an extra other
+ * version.
+ */
+static uint32_t select_preferred_version(const uint32_t *preferred_versions,
+ size_t preferred_versionslen,
+ uint32_t chosen_version,
+ const uint8_t *other_versions,
+ size_t other_versionslen,
+ uint32_t fallback_version) {
+ size_t i, j;
+
+ if (!preferred_versionslen ||
+ (!other_versionslen && chosen_version == fallback_version)) {
+ return fallback_version;
+ }
+
+ for (i = 0; i < preferred_versionslen; ++i) {
+ if (preferred_versions[i] == chosen_version) {
+ return chosen_version;
+ }
+ for (j = 0; j < other_versionslen; j += sizeof(uint32_t)) {
+ if (preferred_versions[i] != ngtcp2_get_uint32(&other_versions[j])) {
+ continue;
+ }
+
+ return preferred_versions[i];
+ }
+ }
+
+ return fallback_version;
+}
+
+/*
* conn_client_validate_transport_params validates |params| as client.
* |params| must be sent with Encrypted Extensions.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
- * NGTCP2_ERR_PROTO
- * Validation against either of original_dcid and retry_scid is
- * failed.
* NGTCP2_ERR_TRANSPORT_PARAM
* params contains preferred address but server chose zero-length
* connection ID.
+ * NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE
+ * Validation against version negotiation parameters failed.
*/
static int
conn_client_validate_transport_params(ngtcp2_conn *conn,
@@ -9551,14 +11074,76 @@ conn_client_validate_transport_params(ngtcp2_conn *conn,
return NGTCP2_ERR_TRANSPORT_PARAM;
}
+ if (params->version_info_present) {
+ if (conn->negotiated_version != params->version_info.chosen_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ assert(vneg_other_versions_includes(conn->vneg.other_versions,
+ conn->vneg.other_versionslen,
+ conn->negotiated_version));
+ } else if (conn->client_chosen_version != conn->negotiated_version ||
+ conn->client_chosen_version !=
+ conn->local.settings.original_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ /* When client reacted upon Version Negotiation */
+ if (conn->local.settings.original_version != conn->client_chosen_version) {
+ assert(params->version_info_present);
+
+ /* Server choose original version after Version Negotiation.
+ Draft does not say this particular case, but this smells like
+ misbehaved server because server should accept original_version
+ in the original connection. */
+ if (conn->local.settings.original_version ==
+ params->version_info.chosen_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ /* Check version downgrade on incompatible version negotiation. */
+ if (params->version_info.other_versionslen == 0) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ if (conn->client_chosen_version !=
+ select_preferred_version(conn->vneg.preferred_versions,
+ conn->vneg.preferred_versionslen,
+ params->version_info.chosen_version,
+ params->version_info.other_versions,
+ params->version_info.other_versionslen,
+ /* fallback_version = */ 0)) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+ }
+
return 0;
}
+uint32_t
+ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
+ const ngtcp2_version_info *version_info) {
+ assert(conn->server);
+ assert(conn->client_chosen_version == version_info->chosen_version);
+
+ return select_preferred_version(
+ conn->vneg.preferred_versions, conn->vneg.preferred_versionslen,
+ version_info->chosen_version, version_info->other_versions,
+ version_info->other_versionslen, version_info->chosen_version);
+}
+
int ngtcp2_conn_set_remote_transport_params(
ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
int rv;
- assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED));
+ /* We expect this function is called once per QUIC connection, but
+ GnuTLS server seems to call TLS extension callback twice if it
+ sends HelloRetryRequest. In practice, same QUIC transport
+ parameters are sent in the 2nd client flight, just returning 0
+ would cause no harm. */
+ if (conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED) {
+ return 0;
+ }
/* Assume that ngtcp2_decode_transport_params sets default value if
active_connection_id_limit is omitted. */
@@ -9574,11 +11159,36 @@ int ngtcp2_conn_set_remote_transport_params(
return NGTCP2_ERR_TRANSPORT_PARAM;
}
- if (params->max_udp_payload_size < 1200) {
+ if (params->max_udp_payload_size < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
return NGTCP2_ERR_TRANSPORT_PARAM;
}
- if (!conn->server) {
+ if (conn->server) {
+ if (params->version_info_present) {
+ if (params->version_info.chosen_version != conn->client_chosen_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ conn->negotiated_version =
+ ngtcp2_conn_server_negotiate_version(conn, &params->version_info);
+ if (conn->negotiated_version != conn->client_chosen_version) {
+ rv = conn_call_version_negotiation(conn, conn->negotiated_version,
+ &conn->rcid);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else {
+ conn->negotiated_version = conn->client_chosen_version;
+ }
+
+ conn->local.transport_params.version_info.chosen_version =
+ conn->negotiated_version;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
+ } else {
rv = conn_client_validate_transport_params(conn, params);
if (rv != 0) {
return rv;
@@ -9596,11 +11206,24 @@ int ngtcp2_conn_set_remote_transport_params(
if ((conn->server && conn->pktns.crypto.tx.ckm) ||
(!conn->server && conn->pktns.crypto.rx.ckm)) {
- conn->remote.transport_params = *params;
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+ conn->remote.transport_params = NULL;
+
+ rv = ngtcp2_transport_params_copy_new(&conn->remote.transport_params,
+ params, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
conn_sync_stream_id_limit(conn);
- conn->tx.max_offset = conn->remote.transport_params.initial_max_data;
+ conn->tx.max_offset = conn->remote.transport_params->initial_max_data;
} else {
- conn->remote.pending_transport_params = *params;
+ assert(!conn->remote.pending_transport_params);
+
+ rv = ngtcp2_transport_params_copy_new(
+ &conn->remote.pending_transport_params, params, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
}
conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED;
@@ -9608,22 +11231,46 @@ int ngtcp2_conn_set_remote_transport_params(
return 0;
}
-void ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn,
- ngtcp2_transport_params *params) {
- if (conn->pktns.crypto.rx.ckm) {
- *params = conn->remote.transport_params;
- } else {
- *params = conn->remote.pending_transport_params;
+int ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn,
+ const uint8_t *data,
+ size_t datalen) {
+ ngtcp2_transport_params params;
+ int rv;
+
+ rv = ngtcp2_decode_transport_params(
+ &params,
+ conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
+ : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
+ data, datalen);
+ if (rv != 0) {
+ return rv;
}
+
+ return ngtcp2_conn_set_remote_transport_params(conn, &params);
}
-void ngtcp2_conn_set_early_remote_transport_params(
- ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
- ngtcp2_transport_params *p = &conn->remote.transport_params;
+const ngtcp2_transport_params *
+ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn) {
+ if (conn->remote.pending_transport_params) {
+ return conn->remote.pending_transport_params;
+ }
+
+ return conn->remote.transport_params;
+}
+
+void ngtcp2_conn_set_early_remote_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params) {
+ ngtcp2_transport_params *p;
+ (void)transport_params_version;
assert(!conn->server);
+ assert(!conn->remote.transport_params);
+
+ /* Assume that all pointer fields in p are NULL */
+ p = ngtcp2_mem_calloc(conn->mem, 1, sizeof(*p));
- memset(p, 0, sizeof(*p));
+ conn->remote.transport_params = p;
p->initial_max_streams_bidi = params->initial_max_streams_bidi;
p->initial_max_streams_uni = params->initial_max_streams_uni;
@@ -9633,8 +11280,38 @@ void ngtcp2_conn_set_early_remote_transport_params(
params->initial_max_stream_data_bidi_remote;
p->initial_max_stream_data_uni = params->initial_max_stream_data_uni;
p->initial_max_data = params->initial_max_data;
+ p->active_connection_id_limit =
+ ngtcp2_max(NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT,
+ params->active_connection_id_limit);
+ p->max_idle_timeout = params->max_idle_timeout;
+ if (!params->max_udp_payload_size) {
+ p->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ } else {
+ p->max_udp_payload_size =
+ ngtcp2_max(NGTCP2_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size);
+ }
+ p->disable_active_migration = params->disable_active_migration;
p->max_datagram_frame_size = params->max_datagram_frame_size;
+ /* These parameters are treated specially. If server accepts early
+ data, it must not set values for these parameters that are
+ smaller than these remembered values. */
+ conn->early.transport_params.initial_max_streams_bidi =
+ params->initial_max_streams_bidi;
+ conn->early.transport_params.initial_max_streams_uni =
+ params->initial_max_streams_uni;
+ conn->early.transport_params.initial_max_stream_data_bidi_local =
+ params->initial_max_stream_data_bidi_local;
+ conn->early.transport_params.initial_max_stream_data_bidi_remote =
+ params->initial_max_stream_data_bidi_remote;
+ conn->early.transport_params.initial_max_stream_data_uni =
+ params->initial_max_stream_data_uni;
+ conn->early.transport_params.initial_max_data = params->initial_max_data;
+ conn->early.transport_params.active_connection_id_limit =
+ params->active_connection_id_limit;
+ conn->early.transport_params.max_datagram_frame_size =
+ params->max_datagram_frame_size;
+
conn_sync_stream_id_limit(conn);
conn->tx.max_offset = p->initial_max_data;
@@ -9643,8 +11320,11 @@ void ngtcp2_conn_set_early_remote_transport_params(
NGTCP2_QLOG_SIDE_REMOTE);
}
-int ngtcp2_conn_set_local_transport_params(
- ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
+int ngtcp2_conn_set_local_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params) {
+ (void)transport_params_version;
+
assert(conn->server);
assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE);
@@ -9652,7 +11332,7 @@ int ngtcp2_conn_set_local_transport_params(
return NGTCP2_ERR_INVALID_STATE;
}
- conn->local.transport_params = *params;
+ conn_set_local_transport_params(conn, params);
return 0;
}
@@ -9661,7 +11341,6 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) {
const ngtcp2_mem *mem = conn->mem;
ngtcp2_transport_params *params = &conn->local.transport_params;
ngtcp2_scid *scident;
- ngtcp2_ksl_it it;
int rv;
assert(1 == ngtcp2_ksl_len(&conn->scid.set));
@@ -9677,32 +11356,21 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) {
params->preferred_address_present = 0;
}
- if (conn->server) {
- if (params->stateless_reset_token_present) {
- it = ngtcp2_ksl_begin(&conn->scid.set);
- scident = ngtcp2_ksl_it_get(&it);
-
- memcpy(scident->token, params->stateless_reset_token,
- NGTCP2_STATELESS_RESET_TOKENLEN);
+ if (conn->server && params->preferred_address_present) {
+ scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
+ if (scident == NULL) {
+ return NGTCP2_ERR_NOMEM;
}
- if (params->preferred_address_present) {
- scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
- if (scident == NULL) {
- return NGTCP2_ERR_NOMEM;
- }
+ ngtcp2_scid_init(scident, 1, &params->preferred_address.cid);
- ngtcp2_scid_init(scident, 1, &params->preferred_address.cid,
- params->preferred_address.stateless_reset_token);
-
- rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident);
- if (rv != 0) {
- ngtcp2_mem_free(mem, scident);
- return rv;
- }
-
- conn->scid.last_seq = 1;
+ rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident);
+ if (rv != 0) {
+ ngtcp2_mem_free(mem, scident);
+ return rv;
}
+
+ conn->scid.last_seq = 1;
}
conn->rx.window = conn->rx.unsent_max_offset = conn->rx.max_offset =
@@ -9712,15 +11380,27 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) {
conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni;
conn->remote.uni.max_streams = params->initial_max_streams_uni;
+ conn->flags |= NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED;
+
ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server,
NGTCP2_QLOG_SIDE_LOCAL);
return 0;
}
-void ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn,
- ngtcp2_transport_params *params) {
- *params = conn->local.transport_params;
+const ngtcp2_transport_params *
+ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn) {
+ return &conn->local.transport_params;
+}
+
+ngtcp2_ssize ngtcp2_conn_encode_local_transport_params(ngtcp2_conn *conn,
+ uint8_t *dest,
+ size_t destlen) {
+ return ngtcp2_encode_transport_params(
+ dest, destlen,
+ conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
+ : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
+ &conn->local.transport_params);
}
int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
@@ -9732,7 +11412,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
return NGTCP2_ERR_STREAM_ID_BLOCKED;
}
- strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
if (strm == NULL) {
return NGTCP2_ERR_NOMEM;
}
@@ -9740,7 +11420,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id,
stream_user_data);
if (rv != 0) {
- ngtcp2_mem_free(conn->mem, strm);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
return rv;
}
@@ -9759,7 +11439,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id,
return NGTCP2_ERR_STREAM_ID_BLOCKED;
}
- strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
if (strm == NULL) {
return NGTCP2_ERR_NOMEM;
}
@@ -9767,7 +11447,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id,
rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id,
stream_user_data);
if (rv != 0) {
- ngtcp2_mem_free(conn->mem, strm);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
return rv;
}
ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD);
@@ -9779,39 +11459,72 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id,
}
ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) {
- ngtcp2_map_entry *me;
-
- me = ngtcp2_map_find(&conn->strms, (uint64_t)stream_id);
- if (me == NULL) {
- return NULL;
- }
-
- return ngtcp2_struct_of(me, ngtcp2_strm, me);
+ return ngtcp2_map_find(&conn->strms, (uint64_t)stream_id);
}
-ngtcp2_ssize ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path,
- ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_ssize *pdatalen,
- uint32_t flags, int64_t stream_id,
- const uint8_t *data, size_t datalen,
- ngtcp2_tstamp ts) {
+ngtcp2_ssize ngtcp2_conn_write_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen,
+ ngtcp2_tstamp ts) {
ngtcp2_vec datav;
datav.len = datalen;
datav.base = (uint8_t *)data;
- return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, pdatalen,
- flags, stream_id, &datav, 1, ts);
+ return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi,
+ dest, destlen, pdatalen, flags,
+ stream_id, &datav, 1, ts);
}
-ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path,
- ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_ssize *pdatalen,
- uint32_t flags, int64_t stream_id,
- const ngtcp2_vec *datav, size_t datavcnt,
- ngtcp2_tstamp ts) {
+static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_vmsg *vmsg,
+ ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_ssize nwrite;
+ int undersized;
+
+ nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest,
+ destlen, vmsg, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (cstat->bytes_in_flight >= cstat->cwnd) {
+ conn->rst.is_cwnd_limited = 1;
+ }
+
+ if (vmsg == NULL && cstat->bytes_in_flight < cstat->cwnd &&
+ conn->tx.strmq_nretrans == 0) {
+ if (conn->local.settings.no_udp_payload_size_shaping) {
+ undersized = (size_t)nwrite < conn->local.settings.max_udp_payload_size;
+ } else {
+ undersized = (size_t)nwrite < conn->dcid.current.max_udp_payload_size;
+ }
+
+ if (undersized) {
+ conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight;
+
+ if (conn->rst.app_limited == 0) {
+ conn->rst.app_limited = cstat->max_udp_payload_size;
+ }
+ }
+ }
+
+ return nwrite;
+}
+
+ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts) {
ngtcp2_vmsg vmsg, *pvmsg;
ngtcp2_strm *strm;
+ int64_t datalen;
if (pdatalen) {
*pdatalen = -1;
@@ -9827,6 +11540,16 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path,
return NGTCP2_ERR_STREAM_SHUT_WR;
}
+ datalen = ngtcp2_vec_len_varint(datav, datavcnt);
+ if (datalen == -1) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if ((uint64_t)datalen > NGTCP2_MAX_VARINT - strm->tx.offset ||
+ (uint64_t)datalen > NGTCP2_MAX_VARINT - conn->tx.offset) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
vmsg.type = NGTCP2_VMSG_TYPE_STREAM;
vmsg.stream.strm = strm;
vmsg.stream.flags = flags;
@@ -9839,50 +11562,67 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path,
pvmsg = NULL;
}
- return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, pvmsg, ts);
+ return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest,
+ destlen, pvmsg, ts);
}
-ngtcp2_ssize ngtcp2_conn_writev_datagram(ngtcp2_conn *conn, ngtcp2_path *path,
- ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, int *paccepted,
- uint32_t flags,
- const ngtcp2_vec *datav,
- size_t datavcnt, ngtcp2_tstamp ts) {
+ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted,
+ uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts) {
ngtcp2_vmsg vmsg;
+ int64_t datalen;
if (paccepted) {
*paccepted = 0;
}
- if (conn->remote.transport_params.max_datagram_frame_size == 0) {
+ if (conn->remote.transport_params == NULL ||
+ conn->remote.transport_params->max_datagram_frame_size == 0) {
return NGTCP2_ERR_INVALID_STATE;
}
- if (conn->remote.transport_params.max_datagram_frame_size <
- ngtcp2_pkt_datagram_framelen(ngtcp2_vec_len(datav, datavcnt))) {
+
+ datalen = ngtcp2_vec_len_varint(datav, datavcnt);
+ if (datalen == -1 || (uint64_t)datalen > SIZE_MAX) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ if (conn->remote.transport_params->max_datagram_frame_size <
+ ngtcp2_pkt_datagram_framelen((size_t)datalen)) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM;
+ vmsg.datagram.dgram_id = dgram_id;
vmsg.datagram.flags = flags;
vmsg.datagram.data = datav;
vmsg.datagram.datacnt = datavcnt;
vmsg.datagram.paccepted = paccepted;
- return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, &vmsg, ts);
+ return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest,
+ destlen, &vmsg, ts);
}
ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
- ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_vmsg *vmsg,
- ngtcp2_tstamp ts) {
+ int pkt_info_version, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) {
ngtcp2_ssize nwrite;
- ngtcp2_pktns *pktns = &conn->pktns;
- size_t origlen = destlen;
+ size_t origlen;
+ size_t origdestlen = destlen;
int rv;
uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
ngtcp2_ssize res = 0;
- size_t server_tx_left;
+ uint64_t server_tx_left;
+ uint64_t datalen;
+ uint64_t write_datalen = 0;
+ int64_t prev_in_pkt_num = -1;
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *rtbent;
+ (void)pkt_info_version;
conn->log.last_ts = ts;
conn->qlog.last_ts = ts;
@@ -9891,25 +11631,47 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
}
+ origlen = destlen =
+ conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
if (!ppe_pending && pi) {
pi->ecn = NGTCP2_ECN_NOT_ECT;
}
+ if (!conn_pacing_pkt_tx_allowed(conn, ts)) {
+ return 0;
+ }
+
switch (conn->state) {
case NGTCP2_CS_CLIENT_INITIAL:
case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED:
nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts);
- if (nwrite < 0) {
+ /* We might be unable to write a packet because of depletion of
+ congestion window budget, perhaps due to packet loss that
+ shrinks the window drastically. */
+ if (nwrite <= 0) {
return nwrite;
}
if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
return nwrite;
}
+
+ assert(nwrite);
+ assert(dest[0] & NGTCP2_HEADER_FORM_BIT);
+ assert(conn->negotiated_version);
+
+ if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) ==
+ NGTCP2_PKT_INITIAL) {
+ /* We have added padding already, but in that case, there is no
+ space left to write 1RTT packet. */
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+
res = nwrite;
dest += nwrite;
destlen -= (size_t)nwrite;
- /* Break here so that we can coalesces Short packets. */
+ /* Break here so that we can coalesces 1RTT packet. */
break;
case NGTCP2_CS_SERVER_INITIAL:
case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
@@ -9917,23 +11679,74 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
if (!ppe_pending) {
if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
- destlen = ngtcp2_min(destlen, server_tx_left);
+ if (server_tx_left == 0) {
+ if (cstat->loss_detection_timer != UINT64_MAX) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer canceled due to amplification limit");
+ cstat->loss_detection_timer = UINT64_MAX;
+ }
+
+ return 0;
+ }
+
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+ }
+
+ if (vmsg) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt);
+ if (datalen == 0 || (datalen > 0 &&
+ (vmsg->stream.strm->tx.max_offset -
+ vmsg->stream.strm->tx.offset) &&
+ (conn->tx.max_offset - conn->tx.offset))) {
+ write_datalen =
+ conn_enforce_flow_control(conn, vmsg->stream.strm, datalen);
+ write_datalen =
+ ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ write_datalen += NGTCP2_STREAM_OVERHEAD;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ write_datalen =
+ ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt) +
+ NGTCP2_DATAGRAM_OVERHEAD;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (conn->in_pktns && write_datalen > 0) {
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ rtbent = ngtcp2_ksl_it_get(&it);
+ prev_in_pkt_num = rtbent->hd.pkt_num;
+ }
+ }
}
- nwrite = conn_write_handshake(conn, pi, dest, destlen, 0, ts);
+ nwrite = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts);
if (nwrite < 0) {
return nwrite;
}
- if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) {
- destlen = origlen;
- } else {
- origlen = destlen;
- }
-
res = nwrite;
dest += nwrite;
destlen -= (size_t)nwrite;
+
+ if (conn->in_pktns && write_datalen > 0) {
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ rtbent = ngtcp2_ksl_it_get(&it);
+ if (rtbent->hd.pkt_num != prev_in_pkt_num &&
+ (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ /* We have added padding already, but in that case, there
+ is no space left to write 1RTT packet. */
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+ }
+ }
}
if (conn->state != NGTCP2_CS_POST_HANDSHAKE &&
conn->pktns.crypto.tx.ckm == NULL) {
@@ -9950,7 +11763,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
return 0;
}
- assert(pktns->crypto.tx.ckm);
+ assert(conn->pktns.crypto.tx.ckm);
if (conn_check_pkt_num_exhausted(conn)) {
return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
@@ -9976,12 +11789,18 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
if (ppe_pending) {
res = conn->pkt.hs_spktlen;
conn->pkt.hs_spktlen = 0;
+ if (conn->pkt.require_padding) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
/* dest and destlen have already been adjusted in ppe in the first
run. They are adjusted for probe packet later. */
- nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
wflags, ts);
goto fin;
} else {
+ conn->pkt.require_padding =
+ (wflags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING);
+
if (conn->state == NGTCP2_CS_POST_HANDSHAKE) {
rv = conn_prepare_key_update(conn, ts);
if (rv != 0) {
@@ -9992,29 +11811,43 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) {
destlen = 0;
} else {
- nwrite = conn_write_path_response(conn, path, pi, dest, destlen, ts);
- if (nwrite) {
- goto fin;
- }
-
- if (conn->pv) {
- nwrite = conn_write_path_challenge(conn, path, pi, dest, destlen, ts);
+ if (res == 0) {
+ nwrite =
+ conn_write_path_response(conn, path, pi, dest, origdestlen, ts);
if (nwrite) {
goto fin;
}
+
+ if (conn->pv) {
+ nwrite =
+ conn_write_path_challenge(conn, path, pi, dest, origdestlen, ts);
+ if (nwrite) {
+ goto fin;
+ }
+ }
+
+ if (conn->pmtud &&
+ (!conn->hs_pktns ||
+ ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) == 0)) {
+ nwrite = conn_write_pmtud_probe(conn, pi, dest, origdestlen, ts);
+ if (nwrite) {
+ goto fin;
+ }
+ }
}
if (conn->server &&
!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
- origlen = ngtcp2_min(origlen, server_tx_left);
- destlen = ngtcp2_min(destlen, server_tx_left);
-
- if (destlen == 0 && conn->cstat.loss_detection_timer != UINT64_MAX) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
- "loss detection timer canceled");
+ origlen = (size_t)ngtcp2_min((uint64_t)origlen, server_tx_left);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+
+ if (server_tx_left == 0 &&
+ conn->cstat.loss_detection_timer != UINT64_MAX) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer canceled due to amplification limit");
conn->cstat.loss_detection_timer = UINT64_MAX;
- conn->cstat.pto_count = 0;
}
}
}
@@ -10025,7 +11858,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
if (conn_handshake_probe_left(conn)) {
destlen = origlen;
}
- nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, 0, ts);
+ nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen,
+ /* write_datalen = */ 0, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -10042,13 +11876,13 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
"transmit probe pkt left=%zu",
conn->pktns.rtb.probe_pkt_left);
- nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
wflags, ts);
goto fin;
}
- nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
wflags, ts);
if (nwrite) {
assert(nwrite != NGTCP2_ERR_NOBUF);
@@ -10056,7 +11890,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
}
if (res == 0) {
- nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_SHORT, ts);
+ nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts);
}
fin:
@@ -10078,24 +11912,27 @@ fin:
static ngtcp2_ssize
conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen, uint8_t pkt_type,
- uint64_t error_code, ngtcp2_tstamp ts) {
+ uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts) {
ngtcp2_pktns *in_pktns = conn->in_pktns;
ngtcp2_pktns *hs_pktns = conn->hs_pktns;
ngtcp2_ssize res = 0, nwrite;
ngtcp2_frame fr;
+ uint8_t flags = NGTCP2_WRITE_PKT_FLAG_NONE;
fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
fr.connection_close.error_code = error_code;
fr.connection_close.frame_type = 0;
- fr.connection_close.reasonlen = 0;
- fr.connection_close.reason = NULL;
+ fr.connection_close.reasonlen = reasonlen;
+ fr.connection_close.reason = (uint8_t *)reason;
if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) &&
pkt_type != NGTCP2_PKT_INITIAL) {
if (in_pktns && conn->server) {
nwrite = ngtcp2_conn_write_single_frame_pkt(
- conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid,
- &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+ conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
+ NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -10109,7 +11946,8 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
hs_pktns->crypto.tx.ckm) {
nwrite = ngtcp2_conn_write_single_frame_pkt(
conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE,
- &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+ NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -10120,8 +11958,12 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
}
+ if (!conn->server && pkt_type == NGTCP2_PKT_INITIAL) {
+ flags = NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+
nwrite = ngtcp2_conn_write_single_frame_pkt(
- conn, pi, dest, destlen, pkt_type, &conn->dcid.current.cid, &fr,
+ conn, pi, dest, destlen, pkt_type, flags, &conn->dcid.current.cid, &fr,
NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
if (nwrite < 0) {
@@ -10137,13 +11979,15 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return res;
}
-ngtcp2_ssize ngtcp2_conn_write_connection_close(
+ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, uint64_t error_code, ngtcp2_tstamp ts) {
+ size_t destlen, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts) {
ngtcp2_pktns *in_pktns = conn->in_pktns;
ngtcp2_pktns *hs_pktns = conn->hs_pktns;
uint8_t pkt_type;
ngtcp2_ssize nwrite;
+ uint64_t server_tx_left;
conn->log.last_ts = ts;
conn->qlog.last_ts = ts;
@@ -10154,9 +11998,10 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(
switch (conn->state) {
case NGTCP2_CS_CLIENT_INITIAL:
+ return NGTCP2_ERR_INVALID_STATE;
case NGTCP2_CS_CLOSING:
case NGTCP2_CS_DRAINING:
- return NGTCP2_ERR_INVALID_STATE;
+ return 0;
default:
break;
}
@@ -10165,13 +12010,20 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(
ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
}
+ destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
if (pi) {
pi->ecn = NGTCP2_ECN_NOT_ECT;
}
+ if (conn->server) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+ }
+
if (conn->state == NGTCP2_CS_POST_HANDSHAKE ||
(conn->server && conn->pktns.crypto.tx.ckm)) {
- pkt_type = NGTCP2_PKT_SHORT;
+ pkt_type = NGTCP2_PKT_1RTT;
} else if (hs_pktns && hs_pktns->crypto.tx.ckm) {
pkt_type = NGTCP2_PKT_HANDSHAKE;
} else if (in_pktns && in_pktns->crypto.tx.ckm) {
@@ -10183,7 +12035,7 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(
}
nwrite = conn_write_connection_close(conn, pi, dest, destlen, pkt_type,
- error_code, ts);
+ error_code, reason, reasonlen, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -10193,12 +12045,14 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(
return nwrite;
}
-ngtcp2_ssize ngtcp2_conn_write_application_close(
+ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts) {
+ size_t destlen, uint64_t app_error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts) {
ngtcp2_ssize nwrite;
ngtcp2_ssize res = 0;
ngtcp2_frame fr;
+ uint64_t server_tx_left;
conn->log.last_ts = ts;
conn->qlog.last_ts = ts;
@@ -10209,9 +12063,10 @@ ngtcp2_ssize ngtcp2_conn_write_application_close(
switch (conn->state) {
case NGTCP2_CS_CLIENT_INITIAL:
+ return NGTCP2_ERR_INVALID_STATE;
case NGTCP2_CS_CLOSING:
case NGTCP2_CS_DRAINING:
- return NGTCP2_ERR_INVALID_STATE;
+ return 0;
default:
break;
}
@@ -10220,16 +12075,23 @@ ngtcp2_ssize ngtcp2_conn_write_application_close(
ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
}
+ destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
if (pi) {
pi->ecn = NGTCP2_ECN_NOT_ECT;
}
+ if (conn->server) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+ }
+
if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) {
nwrite = conn_write_connection_close(conn, pi, dest, destlen,
conn->hs_pktns->crypto.tx.ckm
? NGTCP2_PKT_HANDSHAKE
: NGTCP2_PKT_INITIAL,
- NGTCP2_APPLICATION_ERROR, ts);
+ NGTCP2_APPLICATION_ERROR, NULL, 0, ts);
if (nwrite < 0) {
return nwrite;
}
@@ -10251,12 +12113,12 @@ ngtcp2_ssize ngtcp2_conn_write_application_close(
fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP;
fr.connection_close.error_code = app_error_code;
fr.connection_close.frame_type = 0;
- fr.connection_close.reasonlen = 0;
- fr.connection_close.reason = NULL;
+ fr.connection_close.reasonlen = reasonlen;
+ fr.connection_close.reason = (uint8_t *)reason;
nwrite = ngtcp2_conn_write_single_frame_pkt(
- conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr,
- NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+ conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
if (nwrite < 0) {
return nwrite;
@@ -10273,6 +12135,92 @@ ngtcp2_ssize ngtcp2_conn_write_application_close(
return res;
}
+static void
+connection_close_error_init(ngtcp2_connection_close_error *ccerr,
+ ngtcp2_connection_close_error_code_type type,
+ uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen) {
+ ccerr->type = type;
+ ccerr->error_code = error_code;
+ ccerr->frame_type = 0;
+ ccerr->reason = (uint8_t *)reason;
+ ccerr->reasonlen = reasonlen;
+}
+
+void ngtcp2_connection_close_error_default(
+ ngtcp2_connection_close_error *ccerr) {
+ connection_close_error_init(ccerr,
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+ NGTCP2_NO_ERROR, NULL, 0);
+}
+
+void ngtcp2_connection_close_error_set_transport_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen) {
+ connection_close_error_init(ccerr,
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+ error_code, reason, reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_transport_error_liberr(
+ ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason,
+ size_t reasonlen) {
+ switch (liberr) {
+ case NGTCP2_ERR_RECV_VERSION_NEGOTIATION:
+ connection_close_error_init(
+ ccerr,
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION,
+ NGTCP2_NO_ERROR, reason, reasonlen);
+
+ return;
+ case NGTCP2_ERR_IDLE_CLOSE:
+ connection_close_error_init(
+ ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE,
+ NGTCP2_NO_ERROR, reason, reasonlen);
+
+ return;
+ };
+
+ ngtcp2_connection_close_error_set_transport_error(
+ ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason,
+ reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_transport_error_tls_alert(
+ ngtcp2_connection_close_error *ccerr, uint8_t tls_alert,
+ const uint8_t *reason, size_t reasonlen) {
+ ngtcp2_connection_close_error_set_transport_error(
+ ccerr, NGTCP2_CRYPTO_ERROR | tls_alert, reason, reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_application_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen) {
+ connection_close_error_init(
+ ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, error_code,
+ reason, reasonlen);
+}
+
+ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+ const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts) {
+ (void)pkt_info_version;
+
+ switch (ccerr->type) {
+ case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT:
+ return ngtcp2_conn_write_connection_close_pkt(
+ conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason,
+ ccerr->reasonlen, ts);
+ case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION:
+ return ngtcp2_conn_write_application_close_pkt(
+ conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason,
+ ccerr->reasonlen, ts);
+ default:
+ return 0;
+ }
+}
+
int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) {
return conn->state == NGTCP2_CS_CLOSING;
}
@@ -10281,47 +12229,45 @@ int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn) {
return conn->state == NGTCP2_CS_DRAINING;
}
-int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
- uint64_t app_error_code) {
+int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm) {
int rv;
- if (!strm->app_error_code) {
- app_error_code = strm->app_error_code;
- }
-
- rv = ngtcp2_map_remove(&conn->strms, strm->me.key);
+ rv = ngtcp2_map_remove(&conn->strms, (ngtcp2_map_key_type)strm->stream_id);
if (rv != 0) {
assert(rv != NGTCP2_ERR_INVALID_ARGUMENT);
return rv;
}
- rv = conn_call_stream_close(conn, strm, app_error_code);
+ rv = conn_call_stream_close(conn, strm);
if (rv != 0) {
goto fin;
}
if (ngtcp2_strm_is_tx_queued(strm)) {
ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe);
+ if (!ngtcp2_strm_streamfrq_empty(strm)) {
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ }
}
fin:
ngtcp2_strm_free(strm);
- ngtcp2_mem_free(conn->mem, strm);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
return rv;
}
-int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm,
- uint64_t app_error_code) {
+int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn,
+ ngtcp2_strm *strm) {
if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) ==
NGTCP2_STRM_FLAG_SHUT_RDWR &&
((strm->flags & NGTCP2_STRM_FLAG_RECV_RST) ||
ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) &&
(((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) &&
(strm->flags & NGTCP2_STRM_FLAG_RST_ACKED)) ||
- (!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST) &&
- ngtcp2_strm_is_all_tx_data_acked(strm)))) {
- return ngtcp2_conn_close_stream(conn, strm, app_error_code);
+ ngtcp2_strm_is_all_tx_data_fin_acked(strm))) {
+ return ngtcp2_conn_close_stream(conn, strm);
}
return 0;
}
@@ -10338,14 +12284,16 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm,
*/
static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm,
uint64_t app_error_code) {
- if (strm->flags & NGTCP2_STRM_FLAG_SENT_RST) {
+ ngtcp2_strm_set_app_error_code(strm, app_error_code);
+
+ if ((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) ||
+ ngtcp2_strm_is_all_tx_data_fin_acked(strm)) {
return 0;
}
/* Set this flag so that we don't accidentally send DATA to this
stream. */
strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST;
- strm->app_error_code = app_error_code;
ngtcp2_strm_streamfrq_clear(strm);
@@ -10381,7 +12329,7 @@ static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm,
}
strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING;
- strm->app_error_code = app_error_code;
+ ngtcp2_strm_set_app_error_code(strm, app_error_code);
return conn_stop_sending(conn, strm, app_error_code);
}
@@ -10393,7 +12341,7 @@ int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id,
strm = ngtcp2_conn_find_stream(conn, stream_id);
if (strm == NULL) {
- return NGTCP2_ERR_STREAM_NOT_FOUND;
+ return 0;
}
rv = conn_shutdown_stream_read(conn, strm, app_error_code);
@@ -10415,7 +12363,7 @@ int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id,
strm = ngtcp2_conn_find_stream(conn, stream_id);
if (strm == NULL) {
- return NGTCP2_ERR_STREAM_NOT_FOUND;
+ return 0;
}
return conn_shutdown_stream_write(conn, strm, app_error_code);
@@ -10427,7 +12375,7 @@ int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id,
strm = ngtcp2_conn_find_stream(conn, stream_id);
if (strm == NULL) {
- return NGTCP2_ERR_STREAM_NOT_FOUND;
+ return 0;
}
return conn_shutdown_stream_read(conn, strm, app_error_code);
@@ -10475,7 +12423,7 @@ int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id,
strm = ngtcp2_conn_find_stream(conn, stream_id);
if (strm == NULL) {
- return NGTCP2_ERR_STREAM_NOT_FOUND;
+ return 0;
}
return conn_extend_max_stream_offset(conn, strm, datalen);
@@ -10503,62 +12451,120 @@ const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) {
return &conn->dcid.current.cid;
}
+const ngtcp2_cid *ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn) {
+ return &conn->rcid;
+}
+
+uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn) {
+ return conn->client_chosen_version;
+}
+
uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) {
- return conn->version;
+ return conn->negotiated_version;
}
-int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
- ngtcp2_pktns *pktns = &conn->pktns;
- ngtcp2_rtb *rtb = &conn->pktns.rtb;
- int rv;
+static int delete_strms_pq_each(void *data, void *ptr) {
+ ngtcp2_conn *conn = ptr;
+ ngtcp2_strm *s = data;
- conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED;
+ if (ngtcp2_strm_is_tx_queued(s)) {
+ ngtcp2_pq_remove(&conn->tx.strmq, &s->pe);
+ if (!ngtcp2_strm_streamfrq_empty(s)) {
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ }
+ }
- rv = ngtcp2_rtb_remove_all(rtb, conn, pktns, &conn->cstat);
- if (rv != 0) {
- return rv;
+ ngtcp2_strm_free(s);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s);
+
+ return 0;
+}
+
+/*
+ * conn_discard_early_data_state discards any connection states which
+ * are altered by any operations during early data transfer.
+ */
+static void conn_discard_early_data_state(ngtcp2_conn *conn) {
+ ngtcp2_frame_chain **pfrc, *frc;
+
+ ngtcp2_rtb_remove_early_data(&conn->pktns.rtb, &conn->cstat);
+
+ ngtcp2_map_each_free(&conn->strms, delete_strms_pq_each, conn);
+ ngtcp2_map_clear(&conn->strms);
+
+ conn->tx.offset = 0;
+
+ conn->rx.unsent_max_offset = conn->rx.max_offset =
+ conn->local.transport_params.initial_max_data;
+
+ conn->remote.bidi.unsent_max_streams = conn->remote.bidi.max_streams =
+ conn->local.transport_params.initial_max_streams_bidi;
+
+ conn->remote.uni.unsent_max_streams = conn->remote.uni.max_streams =
+ conn->local.transport_params.initial_max_streams_uni;
+
+ if (conn->server) {
+ conn->local.bidi.next_stream_id = 1;
+ conn->local.uni.next_stream_id = 3;
+ } else {
+ conn->local.bidi.next_stream_id = 0;
+ conn->local.uni.next_stream_id = 2;
}
- return rv;
+ for (pfrc = &conn->pktns.tx.frq; *pfrc;) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ }
}
-void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
- ngtcp2_duration ack_delay, ngtcp2_tstamp ts) {
- ngtcp2_conn_stat *cstat = &conn->cstat;
- ngtcp2_duration min_rtt;
+void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
+ if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) {
+ return;
+ }
+
+ conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED;
- rtt = ngtcp2_max(rtt, NGTCP2_GRANULARITY);
+ conn_discard_early_data_state(conn);
+}
- cstat->latest_rtt = rtt;
+int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+ ngtcp2_duration ack_delay, ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
if (cstat->min_rtt == UINT64_MAX) {
+ cstat->latest_rtt = rtt;
cstat->min_rtt = rtt;
cstat->smoothed_rtt = rtt;
cstat->rttvar = rtt / 2;
cstat->first_rtt_sample_ts = ts;
} else {
- min_rtt = ngtcp2_min(cstat->min_rtt, rtt);
if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) {
+ assert(conn->remote.transport_params);
+
ack_delay =
- ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay);
- } else if (ack_delay > 0 && rtt < cstat->min_rtt + ack_delay) {
+ ngtcp2_min(ack_delay, conn->remote.transport_params->max_ack_delay);
+ } else if (ack_delay > 0 && rtt >= cstat->min_rtt &&
+ rtt < cstat->min_rtt + ack_delay) {
/* Ignore RTT sample if adjusting ack_delay causes the sample
less than min_rtt before handshake confirmation. */
ngtcp2_log_info(
&conn->log, NGTCP2_LOG_EVENT_RCV,
"ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64
" min_rtt=%" PRIu64 " ack_delay=%" PRIu64,
- (uint64_t)(rtt / NGTCP2_MILLISECONDS),
- (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS),
- (uint64_t)(ack_delay / NGTCP2_MILLISECONDS));
- return;
+ rtt / NGTCP2_MILLISECONDS, cstat->min_rtt / NGTCP2_MILLISECONDS,
+ ack_delay / NGTCP2_MILLISECONDS);
+ return NGTCP2_ERR_INVALID_ARGUMENT;
}
- if (rtt > min_rtt + ack_delay) {
+ cstat->latest_rtt = rtt;
+ cstat->min_rtt = ngtcp2_min(cstat->min_rtt, rtt);
+
+ if (rtt >= cstat->min_rtt + ack_delay) {
rtt -= ack_delay;
}
- cstat->min_rtt = min_rtt;
cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt
? rtt - cstat->smoothed_rtt
: cstat->smoothed_rtt - rtt)) /
@@ -10566,56 +12572,91 @@ void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8;
}
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
- "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64
- " smoothed_rtt=%" PRIu64 " rttvar=%" PRIu64
- " ack_delay=%" PRIu64,
- (uint64_t)(cstat->latest_rtt / NGTCP2_MILLISECONDS),
- (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS),
- cstat->smoothed_rtt / NGTCP2_MILLISECONDS,
- cstat->rttvar / NGTCP2_MILLISECONDS,
- (uint64_t)(ack_delay / NGTCP2_MILLISECONDS));
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " smoothed_rtt=%" PRIu64
+ " rttvar=%" PRIu64 " ack_delay=%" PRIu64,
+ cstat->latest_rtt / NGTCP2_MILLISECONDS,
+ cstat->min_rtt / NGTCP2_MILLISECONDS,
+ cstat->smoothed_rtt / NGTCP2_MILLISECONDS,
+ cstat->rttvar / NGTCP2_MILLISECONDS, ack_delay / NGTCP2_MILLISECONDS);
+
+ return 0;
}
-void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) {
+void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn,
+ int conn_stat_version,
+ ngtcp2_conn_stat *cstat) {
+ (void)conn_stat_version;
+
*cstat = conn->cstat;
}
-static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn,
- ngtcp2_tstamp *pts,
- const ngtcp2_tstamp *times) {
+static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn,
+ ngtcp2_tstamp *ploss_time,
+ ngtcp2_pktns **ppktns) {
+ ngtcp2_pktns *const ns[] = {conn->hs_pktns, &conn->pktns};
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_duration *loss_time = cstat->loss_time;
+ ngtcp2_tstamp earliest_loss_time = loss_time[NGTCP2_PKTNS_ID_INITIAL];
+ ngtcp2_pktns *pktns = conn->in_pktns;
+ size_t i;
+
+ for (i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+ if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 ||
+ loss_time[i] >= earliest_loss_time) {
+ continue;
+ }
+
+ earliest_loss_time = loss_time[i];
+ pktns = ns[i];
+ }
+
+ if (ploss_time) {
+ *ploss_time = earliest_loss_time;
+ }
+ if (ppktns) {
+ *ppktns = pktns;
+ }
+}
+
+static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns};
- ngtcp2_pktns *res = ns[0];
size_t i;
- ngtcp2_tstamp earliest_ts = times[NGTCP2_PKTNS_ID_INITIAL];
+ ngtcp2_tstamp earliest_ts = UINT64_MAX, t;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_tstamp *times = cstat->last_tx_pkt_ts;
+ ngtcp2_duration duration =
+ compute_pto(cstat->smoothed_rtt, cstat->rttvar, /* max_ack_delay = */ 0) *
+ (1ULL << cstat->pto_count);
- for (i = NGTCP2_PKTNS_ID_HANDSHAKE; i < NGTCP2_PKTNS_ID_MAX; ++i) {
- if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 ||
+ for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) {
+ if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 ||
(times[i] == UINT64_MAX ||
- (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) ||
(i == NGTCP2_PKTNS_ID_APPLICATION &&
!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
continue;
}
- earliest_ts = times[i];
- res = ns[i];
- }
+ t = times[i] + duration;
- if (res == NULL && !conn->server) {
- earliest_ts = UINT64_MAX;
+ if (i == NGTCP2_PKTNS_ID_APPLICATION) {
+ assert(conn->remote.transport_params);
+ t += conn->remote.transport_params->max_ack_delay *
+ (1ULL << cstat->pto_count);
+ }
- if (conn->hs_pktns && conn->hs_pktns->crypto.tx.ckm) {
- res = conn->hs_pktns;
- } else {
- res = conn->in_pktns;
+ if (t < earliest_ts) {
+ earliest_ts = t;
}
}
- if (pts) {
- *pts = earliest_ts;
+ if (earliest_ts == UINT64_MAX) {
+ return ts + duration;
}
- return res;
+
+ return earliest_ts;
}
void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
@@ -10624,11 +12665,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
ngtcp2_pktns *in_pktns = conn->in_pktns;
ngtcp2_pktns *hs_pktns = conn->hs_pktns;
ngtcp2_pktns *pktns = &conn->pktns;
- ngtcp2_pktns *earliest_pktns;
ngtcp2_tstamp earliest_loss_time;
- ngtcp2_tstamp last_tx_pkt_ts;
- conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time);
+ conn_get_loss_time_and_pktns(conn, &earliest_loss_time, NULL);
if (earliest_loss_time != UINT64_MAX) {
cstat->loss_detection_timer = earliest_loss_time;
@@ -10639,9 +12678,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
return;
}
- if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) &&
- (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) &&
- (pktns->rtb.num_retransmittable == 0 ||
+ if ((!in_pktns || in_pktns->rtb.num_pto_eliciting == 0) &&
+ (!hs_pktns || hs_pktns->rtb.num_pto_eliciting == 0) &&
+ (pktns->rtb.num_pto_eliciting == 0 ||
!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) &&
(conn->server ||
(conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
@@ -10655,24 +12694,14 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
return;
}
- earliest_pktns =
- conn_get_earliest_pktns(conn, &last_tx_pkt_ts, cstat->last_tx_pkt_ts);
-
- assert(earliest_pktns);
-
- if (last_tx_pkt_ts == UINT64_MAX) {
- last_tx_pkt_ts = ts;
- }
-
- timeout = conn_compute_pto(conn, earliest_pktns) * (1ULL << cstat->pto_count);
+ cstat->loss_detection_timer = conn_get_earliest_pto_expiry(conn, ts);
- cstat->loss_detection_timer = last_tx_pkt_ts + timeout;
+ timeout =
+ cstat->loss_detection_timer > ts ? cstat->loss_detection_timer - ts : 0;
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
- "loss_detection_timer=%" PRIu64 " last_tx_pkt_ts=%" PRIu64
- " timeout=%" PRIu64,
- cstat->loss_detection_timer, last_tx_pkt_ts,
- (uint64_t)(timeout / NGTCP2_MILLISECONDS));
+ "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64,
+ cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS);
}
int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
@@ -10681,9 +12710,7 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
ngtcp2_pktns *in_pktns = conn->in_pktns;
ngtcp2_pktns *hs_pktns = conn->hs_pktns;
ngtcp2_tstamp earliest_loss_time;
- ngtcp2_pktns *loss_pktns =
- conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time);
- ngtcp2_pktns *earliest_pktns;
+ ngtcp2_pktns *loss_pktns = NULL;
conn->log.last_ts = ts;
conn->qlog.last_ts = ts;
@@ -10702,10 +12729,14 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
return 0;
}
+ conn_get_loss_time_and_pktns(conn, &earliest_loss_time, &loss_pktns);
+
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
"loss detection timer fired");
if (earliest_loss_time != UINT64_MAX) {
+ assert(loss_pktns);
+
rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts);
if (rv != 0) {
return rv;
@@ -10714,35 +12745,26 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
return 0;
}
- if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ if (!conn->server && !conn_is_handshake_completed(conn)) {
if (hs_pktns->crypto.tx.ckm) {
hs_pktns->rtb.probe_pkt_left = 1;
} else {
in_pktns->rtb.probe_pkt_left = 1;
}
} else {
- earliest_pktns = conn_get_earliest_pktns(conn, NULL, cstat->last_tx_pkt_ts);
+ if (in_pktns && in_pktns->rtb.num_pto_eliciting) {
+ in_pktns->rtb.probe_pkt_left = 1;
- assert(earliest_pktns);
+ assert(hs_pktns);
- switch (earliest_pktns->rtb.pktns_id) {
- case NGTCP2_PKTNS_ID_INITIAL:
- assert(in_pktns);
- in_pktns->rtb.probe_pkt_left = 1;
- if (!conn->server) {
- break;
+ if (conn->server && hs_pktns->rtb.num_pto_eliciting) {
+ /* let server coalesce packets */
+ hs_pktns->rtb.probe_pkt_left = 1;
}
- /* fall through for server so that it can coalesce packets. */
- /* fall through */
- case NGTCP2_PKTNS_ID_HANDSHAKE:
- assert(hs_pktns);
+ } else if (hs_pktns && hs_pktns->rtb.num_pto_eliciting) {
hs_pktns->rtb.probe_pkt_left = 1;
- break;
- case NGTCP2_PKTNS_ID_APPLICATION:
+ } else {
conn->pktns.rtb.probe_pkt_left = 2;
- break;
- default:
- assert(0);
}
}
@@ -10756,6 +12778,34 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
return 0;
}
+static int conn_buffer_crypto_data(ngtcp2_conn *conn, const uint8_t **pdata,
+ ngtcp2_pktns *pktns, const uint8_t *data,
+ size_t datalen) {
+ int rv;
+ ngtcp2_buf_chain **pbufchain = &pktns->crypto.tx.data;
+
+ if (*pbufchain) {
+ for (; (*pbufchain)->next; pbufchain = &(*pbufchain)->next)
+ ;
+
+ if (ngtcp2_buf_left(&(*pbufchain)->buf) < datalen) {
+ pbufchain = &(*pbufchain)->next;
+ }
+ }
+
+ if (!*pbufchain) {
+ rv = ngtcp2_buf_chain_new(pbufchain, ngtcp2_max(1024, datalen), conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ *pdata = (*pbufchain)->buf.last;
+ (*pbufchain)->buf.last = ngtcp2_cpymem((*pbufchain)->buf.last, data, datalen);
+
+ return 0;
+}
+
int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
ngtcp2_crypto_level crypto_level,
const uint8_t *data, const size_t datalen) {
@@ -10784,7 +12834,12 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+ rv = conn_buffer_crypto_data(conn, &data, pktns, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -10799,7 +12854,7 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc);
if (rv != 0) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
return rv;
}
@@ -10813,24 +12868,18 @@ int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token,
size_t tokenlen) {
int rv;
ngtcp2_frame_chain *nfrc;
- uint8_t *p;
+ ngtcp2_vec tokenv = {(uint8_t *)token, tokenlen};
assert(conn->server);
assert(token);
assert(tokenlen);
- rv = ngtcp2_frame_chain_extralen_new(&nfrc, tokenlen, conn->mem);
+ rv = ngtcp2_frame_chain_new_token_objalloc_new(
+ &nfrc, &tokenv, &conn->frc_objalloc, conn->mem);
if (rv != 0) {
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_NEW_TOKEN;
-
- p = (uint8_t *)nfrc + sizeof(*nfrc);
- memcpy(p, token, tokenlen);
-
- ngtcp2_vec_init(&nfrc->fr.new_token.token, p, tokenlen);
-
nfrc->next = conn->pktns.tx.frq;
conn->pktns.tx.frq = nfrc;
@@ -10853,11 +12902,20 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) {
return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe);
}
+static int conn_has_uncommited_preferred_address_cid(ngtcp2_conn *conn) {
+ return conn->server &&
+ !(conn->flags & NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED) &&
+ conn->oscid.datalen &&
+ conn->local.transport_params.preferred_address_present;
+}
+
size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) {
- return ngtcp2_ksl_len(&conn->scid.set);
+ return ngtcp2_ksl_len(&conn->scid.set) +
+ (size_t)conn_has_uncommited_preferred_address_cid(conn);
}
size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) {
+ ngtcp2_cid *origdest = dest;
ngtcp2_ksl_it it;
ngtcp2_scid *scid;
@@ -10867,7 +12925,11 @@ size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) {
*dest++ = scid->cid;
}
- return ngtcp2_ksl_len(&conn->scid.set);
+ if (conn_has_uncommited_preferred_address_cid(conn)) {
+ *dest++ = conn->local.transport_params.preferred_address.cid;
+ }
+
+ return (size_t)(dest - origdest);
}
size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) {
@@ -10889,7 +12951,7 @@ size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) {
}
}
- n += ngtcp2_ringbuf_len(&conn->dcid.retired);
+ n += ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
return n;
}
@@ -10899,9 +12961,10 @@ static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest,
dest->seq = src->seq;
dest->cid = src->cid;
ngtcp2_path_storage_init2(&dest->ps, &src->ps.path);
- dest->token_present =
- (uint8_t)!ngtcp2_check_invalid_stateless_reset_token(src->token);
- memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ if ((dest->token_present =
+ (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) != 0)) {
+ memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ }
}
size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
@@ -10930,9 +12993,9 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
}
}
- len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+ len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
copy_dcid_to_cid_token(dest, dcid);
++dest;
}
@@ -10943,71 +13006,148 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) {
ngtcp2_addr *dest = &conn->dcid.current.ps.path.local;
- assert(addr->addrlen <= sizeof(conn->dcid.current.ps.local_addrbuf));
+ assert(addr->addrlen <=
+ (ngtcp2_socklen)sizeof(conn->dcid.current.ps.local_addrbuf));
ngtcp2_addr_copy(dest, addr);
}
-void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) {
- ngtcp2_addr *dest = &conn->dcid.current.ps.path.remote;
-
- assert(addr->addrlen <= sizeof(conn->dcid.current.ps.remote_addrbuf));
- ngtcp2_addr_copy(dest, addr);
+void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, void *path_user_data) {
+ conn->dcid.current.ps.path.user_data = path_user_data;
}
const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) {
return &conn->dcid.current.ps.path;
}
-int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
- ngtcp2_tstamp ts) {
- int rv;
- ngtcp2_dcid *dcid;
+size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn) {
+ return conn->local.settings.max_udp_payload_size;
+}
- assert(!conn->server);
+size_t ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn) {
+ if (conn->local.settings.no_udp_payload_size_shaping) {
+ return ngtcp2_conn_get_max_udp_payload_size(conn);
+ }
- conn->log.last_ts = ts;
- conn->qlog.last_ts = ts;
+ return conn->dcid.current.max_udp_payload_size;
+}
- if (conn->remote.transport_params.disable_active_migration ||
+static int conn_initiate_migration_precheck(ngtcp2_conn *conn,
+ const ngtcp2_addr *local_addr) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) ||
+ conn->remote.transport_params->disable_active_migration ||
conn->dcid.current.cid.datalen == 0 ||
- !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) {
+ (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR))) {
return NGTCP2_ERR_INVALID_STATE;
}
- if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
return NGTCP2_ERR_CONN_ID_BLOCKED;
}
- if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, local_addr)) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ return 0;
+}
+
+int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_dcid *dcid;
+
+ assert(!conn->server);
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
- rv = conn_stop_pv(conn, ts);
+ rv = conn_initiate_migration_precheck(conn, &path->local);
if (rv != 0) {
return rv;
}
+ ngtcp2_conn_stop_pmtud(conn);
+
+ if (conn->pv) {
+ rv = conn_abort_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
if (rv != 0) {
return rv;
}
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ngtcp2_dcid_set_path(dcid, path);
+
ngtcp2_dcid_copy(&conn->dcid.current, dcid);
- ngtcp2_path_copy(&conn->dcid.current.ps.path, path);
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
rv = conn_call_activate_dcid(conn, &conn->dcid.current);
if (rv != 0) {
return rv;
}
- conn_reset_congestion_state(conn);
+ conn_reset_congestion_state(conn, ts);
conn_reset_ecn_validation_state(conn);
+ if (!conn->local.settings.no_pmtud) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
return 0;
}
+int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_dcid *dcid;
+ ngtcp2_duration pto, initial_pto, timeout;
+ ngtcp2_pv *pv;
+
+ assert(!conn->server);
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ rv = conn_initiate_migration_precheck(conn, &path->local);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->pv) {
+ rv = conn_abort_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ngtcp2_dcid_set_path(dcid, path);
+
+ pto = conn_compute_pto(conn, &conn->pktns);
+ initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, initial_pto);
+
+ rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+ conn->pv = pv;
+
+ return conn_call_activate_dcid(conn, &pv->dcid);
+}
+
uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) {
return conn->local.uni.max_streams;
}
@@ -11016,6 +13156,17 @@ uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) {
return conn->tx.max_offset - conn->tx.offset;
}
+uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn,
+ int64_t stream_id) {
+ ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ if (strm == NULL) {
+ return 0;
+ }
+
+ return strm->tx.max_offset - strm->tx.offset;
+}
+
uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn) {
uint64_t n = ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id);
@@ -11031,6 +13182,17 @@ uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) {
: conn->local.uni.max_streams - n + 1;
}
+uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) {
+ uint64_t bytes_in_flight = conn->cstat.bytes_in_flight;
+ uint64_t cwnd = conn_get_cwnd(conn);
+
+ if (cwnd > bytes_in_flight) {
+ return cwnd - bytes_in_flight;
+ }
+
+ return 0;
+}
+
ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) {
ngtcp2_duration trpto;
ngtcp2_duration idle_timeout;
@@ -11038,33 +13200,30 @@ ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) {
/* TODO Remote max_idle_timeout becomes effective after handshake
completion. */
- if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) ||
- conn->remote.transport_params.max_idle_timeout == 0 ||
+ if (!conn_is_handshake_completed(conn) ||
+ conn->remote.transport_params->max_idle_timeout == 0 ||
(conn->local.transport_params.max_idle_timeout &&
conn->local.transport_params.max_idle_timeout <
- conn->remote.transport_params.max_idle_timeout)) {
+ conn->remote.transport_params->max_idle_timeout)) {
idle_timeout = conn->local.transport_params.max_idle_timeout;
} else {
- idle_timeout = conn->remote.transport_params.max_idle_timeout;
+ idle_timeout = conn->remote.transport_params->max_idle_timeout;
}
if (idle_timeout == 0) {
return UINT64_MAX;
}
- trpto = 3 * conn_compute_pto(
- conn, (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)
- ? &conn->pktns
- : conn->hs_pktns);
+ trpto = 3 * conn_compute_pto(conn, conn_is_handshake_completed(conn)
+ ? &conn->pktns
+ : conn->hs_pktns);
return conn->idle_ts + ngtcp2_max(idle_timeout, trpto);
}
ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) {
- return conn_compute_pto(conn,
- (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)
- ? &conn->pktns
- : conn->hs_pktns);
+ return conn_compute_pto(
+ conn, conn_is_handshake_completed(conn) ? &conn->pktns : conn->hs_pktns);
}
void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn,
@@ -11116,9 +13275,9 @@ void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn,
conn->crypto.tls_native_handle = tls_native_handle;
}
-void ngtcp2_conn_get_connection_close_error_code(
- ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec) {
- *ccec = conn->rx.ccec;
+void ngtcp2_conn_get_connection_close_error(
+ ngtcp2_conn *conn, ngtcp2_connection_close_error *ccerr) {
+ *ccerr = conn->rx.ccerr;
}
void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr) {
@@ -11129,6 +13288,14 @@ int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) {
return conn->crypto.tls_error;
}
+void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert) {
+ conn->crypto.tls_alert = alert;
+}
+
+uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn) {
+ return conn->crypto.tls_alert;
+}
+
int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) {
return conn_local_stream(conn, stream_id);
}
@@ -11152,6 +13319,71 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id,
return 0;
}
+void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ if (!(conn->cstat.pacing_rate > 0) || conn->tx.pacing.pktlen == 0) {
+ return;
+ }
+
+ conn->tx.pacing.next_ts =
+ ts + (ngtcp2_duration)((double)conn->tx.pacing.pktlen /
+ conn->cstat.pacing_rate);
+ conn->tx.pacing.pktlen = 0;
+}
+
+size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) {
+ return conn->cstat.send_quantum;
+}
+
+int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
+ size_t i;
+
+ if (conn->dcid.retire_unacked.len >=
+ sizeof(conn->dcid.retire_unacked.seqs) /
+ sizeof(conn->dcid.retire_unacked.seqs[0])) {
+ return NGTCP2_ERR_CONNECTION_ID_LIMIT;
+ }
+
+ /* Make sure that we do not have a duplicate */
+ for (i = 0; i < conn->dcid.retire_unacked.len; ++i) {
+ if (conn->dcid.retire_unacked.seqs[i] == seq) {
+ assert(0);
+ }
+ }
+
+ conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq;
+
+ return 0;
+}
+
+void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
+ size_t i;
+
+ for (i = 0; i < conn->dcid.retire_unacked.len; ++i) {
+ if (conn->dcid.retire_unacked.seqs[i] != seq) {
+ continue;
+ }
+
+ if (i != conn->dcid.retire_unacked.len - 1) {
+ conn->dcid.retire_unacked.seqs[i] =
+ conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1];
+ }
+
+ --conn->dcid.retire_unacked.len;
+
+ return;
+ }
+}
+
+size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) {
+ ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ if (strm == NULL) {
+ return 0;
+ }
+
+ return strm->tx.loss_count;
+}
+
void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
const ngtcp2_path *path,
const uint8_t *data) {
@@ -11159,16 +13391,24 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
memcpy(pcent->data, data, sizeof(pcent->data));
}
-void ngtcp2_settings_default(ngtcp2_settings *settings) {
+void ngtcp2_settings_default_versioned(int settings_version,
+ ngtcp2_settings *settings) {
+ (void)settings_version;
+
memset(settings, 0, sizeof(*settings));
settings->cc_algo = NGTCP2_CC_ALGO_CUBIC;
settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
settings->ack_thresh = 2;
+ settings->max_udp_payload_size = 1500 - 48;
+ settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT;
}
-void ngtcp2_transport_params_default(ngtcp2_transport_params *params) {
+void ngtcp2_transport_params_default_versioned(
+ int transport_params_version, ngtcp2_transport_params *params) {
+ (void)transport_params_version;
+
memset(params, 0, sizeof(*params));
- params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE;
+ params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
params->active_connection_id_limit =
@@ -11181,9 +13421,10 @@ void ngtcp2_transport_params_default(ngtcp2_transport_params *params) {
here. */
ngtcp2_ssize ngtcp2_pkt_write_connection_close(
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
- const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt,
- const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx,
- const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv,
+ ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx) {
ngtcp2_pkt_hd hd;
ngtcp2_crypto_km ckm;
@@ -11223,6 +13464,8 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close(
fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
fr.connection_close.error_code = error_code;
+ fr.connection_close.reasonlen = reasonlen;
+ fr.connection_close.reason = (uint8_t *)reason;
rv = ngtcp2_ppe_encode_frame(&ppe, &fr);
if (rv != 0) {
@@ -11234,3 +13477,26 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close(
}
int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); }
+
+uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
+ size_t preferred_versionslen,
+ const uint32_t *offered_versions,
+ size_t offered_versionslen) {
+ size_t i, j;
+
+ if (!preferred_versionslen || !offered_versionslen) {
+ return 0;
+ }
+
+ for (i = 0; i < preferred_versionslen; ++i) {
+ assert(ngtcp2_is_supported_version(preferred_versions[i]));
+
+ for (j = 0; j < offered_versionslen; ++j) {
+ if (preferred_versions[i] == offered_versions[j]) {
+ return preferred_versions[i];
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h
index 825e4502e13..b1c6564175d 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h
@@ -36,14 +36,16 @@
#include "ngtcp2_acktr.h"
#include "ngtcp2_rtb.h"
#include "ngtcp2_strm.h"
-#include "ngtcp2_mem.h"
#include "ngtcp2_idtr.h"
#include "ngtcp2_str.h"
#include "ngtcp2_pkt.h"
#include "ngtcp2_log.h"
#include "ngtcp2_pq.h"
#include "ngtcp2_cc.h"
+#include "ngtcp2_bbr.h"
+#include "ngtcp2_bbr2.h"
#include "ngtcp2_pv.h"
+#include "ngtcp2_pmtud.h"
#include "ngtcp2_cid.h"
#include "ngtcp2_buf.h"
#include "ngtcp2_ppe.h"
@@ -117,6 +119,21 @@ typedef enum {
packets sent in NGTCP2_ECN_STATE_TESTING period. */
#define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10
+/* NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN is the maximum length
+ of reason phrase to remember. If the received reason phrase is
+ longer than this value, it is truncated. */
+#define NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN 1024
+
+/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00u
+/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other
+ than Initial packet should be padded. Initial packet might be
+ padded based on QUIC requirement regardless of this flag. */
+#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01u
+/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come
+ and it should be encoded into the current packet. */
+#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02u
+
/*
* ngtcp2_max_frame is defined so that it covers the largest ACK
* frame.
@@ -140,52 +157,69 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
const uint8_t *data);
/* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_CONN_FLAG_NONE 0x00
-/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake
- completed. */
-#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01
+#define NGTCP2_CONN_FLAG_NONE 0x00u
+/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when TLS stack declares
+ that TLS handshake has completed. The condition of this
+ declaration varies between TLS implementations and this flag does
+ not indicate the completion of QUIC handshake. Some
+ implementations declare TLS handshake completion as server when
+ they write off Server Finished and before deriving application rx
+ secret. */
+#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01u
/* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is
negotiated. This is only used for client. */
-#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02
+#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02u
/* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport
parameters are received. */
-#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04
-/* NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT is set when a protected packet
- is received, and decrypted successfully. This flag is used to stop
- retransmitting handshake packets. It might be replaced with an
- another mechanism when we implement key update. */
-#define NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT 0x08
+#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04u
+/* NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED is set when a
+ local transport parameters are applied. */
+#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08u
/* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry
packet. */
-#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10
+#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10u
/* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is
rejected by a peer. */
-#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20
+#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20u
+/* NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED is set when the expired
+ keep-alive timer has been cancelled. */
+#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40u
/* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint
confirmed completion of handshake. */
-#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80
+#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80u
/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the
library transitions its state to "post handshake". */
-#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100
+#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100u
/* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early
handshake retransmission has done when server receives overlapping
Initial crypto data. */
-#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200
+#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200u
+/* NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT indicates that the local endpoint
+ sends a QUIC packet without Fixed Bit set if a remote endpoint
+ supports Greasing QUIC Bit extension. */
+#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400u
/* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update is
not confirmed by the local endpoint. That is, it has not received
ACK frame which acknowledges packet which is encrypted with new
key. */
-#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800
+#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800u
/* NGTCP2_CONN_FLAG_PPE_PENDING is set when
NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state of
ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */
-#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000
+#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000u
/* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer
should be restarted on next write. */
-#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000
+#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000u
/* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer
verified client address. This flag is only used by client. */
-#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000
+#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000u
+/* NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED indicates that an early key is
+ installed. conn->early.ckm cannot be used for this purpose because
+ it might be discarded when a certain condition is met. */
+#define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000u
+/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local
+ endpoint has initiated key update. */
+#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u
typedef struct ngtcp2_crypto_data {
ngtcp2_buf buf;
@@ -264,9 +298,9 @@ typedef struct ngtcp2_pktns {
struct {
/* ect0, ect1, ce are the ECN counts received in the latest
ACK frame. */
- size_t ect0;
- size_t ect1;
- size_t ce;
+ uint64_t ect0;
+ uint64_t ect1;
+ uint64_t ce;
} ack;
} ecn;
} rx;
@@ -283,6 +317,8 @@ typedef struct ngtcp2_pktns {
ngtcp2_crypto_km *ckm;
/* hp_ctx is cipher context for packet header protection. */
ngtcp2_crypto_cipher_ctx hp_ctx;
+ /* data is the submitted crypto data. */
+ ngtcp2_buf_chain *data;
} tx;
struct {
@@ -308,16 +344,28 @@ typedef enum ngtcp2_ecn_state {
NGTCP2_ECN_STATE_CAPABLE,
} ngtcp2_ecn_state;
+ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE,
+ sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE,
+ sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
+ sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(path_challenge, 4,
+ sizeof(ngtcp2_path_challenge_entry));
+
+ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent);
+
struct ngtcp2_conn {
+ ngtcp2_objalloc frc_objalloc;
+ ngtcp2_objalloc rtb_entry_objalloc;
+ ngtcp2_objalloc strm_objalloc;
ngtcp2_conn_state state;
ngtcp2_callbacks callbacks;
/* rcid is a connection ID present in Initial or 0-RTT packet from
client as destination connection ID. Server uses this field to
check that duplicated Initial or 0-RTT packet are indeed sent to
- this connection. It is also sent to client as
- original_destination_connection_id transport parameter. Client
- uses this field to validate original_destination_connection_id
- transport parameter if no Retry packet is involved. */
+ this connection. Client uses this field to validate
+ original_destination_connection_id transport parameter. */
ngtcp2_cid rcid;
/* oscid is the source connection ID initially used by the local
endpoint. */
@@ -335,21 +383,25 @@ struct ngtcp2_conn {
ngtcp2_dcid current;
/* bound is a set of destination connection IDs which are bound to
particular paths. These paths are not validated yet. */
- ngtcp2_ringbuf bound;
+ ngtcp2_static_ringbuf_dcid_bound bound;
/* unused is a set of unused CID received from peer. */
- ngtcp2_ringbuf unused;
+ ngtcp2_static_ringbuf_dcid_unused unused;
/* retired is a set of CID retired by local endpoint. Keep them
in 3*PTO to catch packets in flight along the old path. */
- ngtcp2_ringbuf retired;
+ ngtcp2_static_ringbuf_dcid_retired retired;
/* seqgap tracks received sequence numbers in order to ignore
retransmitted duplicated NEW_CONNECTION_ID frame. */
ngtcp2_gaptr seqgap;
/* retire_prior_to is the largest retire_prior_to received so
far. */
uint64_t retire_prior_to;
- /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames
- queued for transmission. */
- size_t num_retire_queued;
+ struct {
+ /* seqs contains sequence number of Connection ID whose
+ retirement is not acknowledged by the remote endpoint yet. */
+ uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2];
+ /* len is the number of sequence numbers that seq contains. */
+ size_t len;
+ } retire_unacked;
/* zerolen_seq is a pseudo sequence number of zero-length
Destination Connection ID in order to distinguish between
them. */
@@ -374,6 +426,9 @@ struct ngtcp2_conn {
struct {
/* strmq contains ngtcp2_strm which has frames to send. */
ngtcp2_pq strmq;
+ /* strmq_nretrans is the number of entries in strmq which has
+ stream data to resent. */
+ size_t strmq_nretrans;
/* ack is ACK frame. The underlying buffer is reused. */
ngtcp2_frame *ack;
/* max_ack_blks is the number of additional ngtcp2_ack_blk which
@@ -399,6 +454,16 @@ struct ngtcp2_conn {
validation period. */
size_t dgram_sent;
} ecn;
+
+ struct {
+ /* pktlen is the number of bytes written before calling
+ ngtcp2_conn_update_pkt_tx_time which resets this field to
+ 0. */
+ size_t pktlen;
+ /* next_ts is the time to send next packet. It is UINT64_MAX if
+ packet pacing is disabled or expired.*/
+ ngtcp2_tstamp next_ts;
+ } pacing;
} tx;
struct {
@@ -415,9 +480,9 @@ struct ngtcp2_conn {
/* window is the connection-level flow control window size. */
uint64_t window;
/* path_challenge stores received PATH_CHALLENGE data. */
- ngtcp2_ringbuf path_challenge;
- /* ccec is the received connection close error code. */
- ngtcp2_connection_close_error_code ccec;
+ ngtcp2_static_ringbuf_path_challenge path_challenge;
+ /* ccerr is the received connection close error. */
+ ngtcp2_connection_close_error ccerr;
} rx;
struct {
@@ -427,6 +492,21 @@ struct ngtcp2_conn {
/* discard_started_ts is the timestamp when the timer to discard
early key has started. Used by server only. */
ngtcp2_tstamp discard_started_ts;
+ /* transport_params is the values remembered by client from the
+ previous session. These are set by
+ ngtcp2_conn_set_early_remote_transport_params(). Server does
+ not use this field. Server must not set values for these
+ parameters that are smaller than the remembered values. */
+ struct {
+ uint64_t initial_max_streams_bidi;
+ uint64_t initial_max_streams_uni;
+ uint64_t initial_max_stream_data_bidi_local;
+ uint64_t initial_max_stream_data_bidi_remote;
+ uint64_t initial_max_stream_data_uni;
+ uint64_t initial_max_data;
+ uint64_t active_connection_id_limit;
+ uint64_t max_datagram_frame_size;
+ } transport_params;
} early;
struct {
@@ -456,11 +536,11 @@ struct ngtcp2_conn {
struct {
/* transport_params is the received transport parameters during
handshake. It is used for Short packet only. */
- ngtcp2_transport_params transport_params;
+ ngtcp2_transport_params *transport_params;
/* pending_transport_params is received transport parameters
during handshake. It is copied to transport_params when 1RTT
key is available. */
- ngtcp2_transport_params pending_transport_params;
+ ngtcp2_transport_params *pending_transport_params;
struct {
ngtcp2_idtr idtr;
/* unsent_max_streams is the maximum number of streams of peer
@@ -519,6 +599,8 @@ struct ngtcp2_conn {
ngtcp2_crypto_aead_ctx retry_aead_ctx;
/* tls_error is TLS related error. */
int tls_error;
+ /* tls_alert is TLS alert generated by the local endpoint. */
+ uint8_t tls_alert;
/* decryption_failure_count is the number of received packets that
fail authentication. */
uint64_t decryption_failure_count;
@@ -533,14 +615,63 @@ struct ngtcp2_conn {
ngtcp2_frame_chain **pfrc;
int pkt_empty;
int hd_logged;
- uint8_t rtb_entry_flags;
- int was_client_initial;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_RTB_ENTRY_FLAG_*. */
+ uint16_t rtb_entry_flags;
ngtcp2_ssize hs_spktlen;
+ int require_padding;
} pkt;
+ struct {
+ /* last_ts is a timestamp when a last packet is sent or received
+ on a current path. */
+ ngtcp2_tstamp last_ts;
+ /* timeout is keep-alive timeout. When it expires, a packet
+ should be sent to a current path to keep connection alive. It
+ might be used to keep NAT binding intact. If 0 is set,
+ keep-alive timer is disabled. */
+ ngtcp2_duration timeout;
+ } keep_alive;
+
+ struct {
+ /* Initial keys for negotiated version. If original version ==
+ negotiated version, these fields are not used. */
+ struct {
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ } rx;
+ struct {
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ } tx;
+ /* version is QUIC version that the above Initial keys are created
+ for. */
+ uint32_t version;
+ /* preferred_versions is the array of versions that are preferred
+ by the local endpoint. Server negotiates one of those versions
+ in this array if a client initially selects a less preferred
+ version. Client uses this field and original_version field to
+ prevent version downgrade attack if it reacted upon Version
+ Negotiation packet. */
+ uint32_t *preferred_versions;
+ /* preferred_versionslen is the number of versions stored in the
+ array pointed by preferred_versions. This field is only used
+ by server. */
+ size_t preferred_versionslen;
+ /* other_versions is the versions that the local endpoint sends in
+ version_information transport parameter. This is the wire
+ image of other_versions field of version_information transport
+ parameter. */
+ uint8_t *other_versions;
+ /* other_versionslen is the length of data pointed by
+ other_versions field. */
+ size_t other_versionslen;
+ } vneg;
+
ngtcp2_map strms;
ngtcp2_conn_stat cstat;
ngtcp2_pv *pv;
+ ngtcp2_pmtud *pmtud;
ngtcp2_log log;
ngtcp2_qlog qlog;
ngtcp2_rst rst;
@@ -550,9 +681,10 @@ struct ngtcp2_conn {
/* idle_ts is the time instant when idle timer started. */
ngtcp2_tstamp idle_ts;
void *user_data;
- uint32_t version;
+ uint32_t client_chosen_version;
+ uint32_t negotiated_version;
/* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */
- uint16_t flags;
+ uint32_t flags;
int server;
};
@@ -583,6 +715,8 @@ typedef struct ngtcp2_vmsg_datagram {
const ngtcp2_vec *data;
/* datacnt is the number of ngtcp2_vec pointed by data. */
size_t datacnt;
+ /* dgram_id is an opaque identifier chosen by an application. */
+ uint64_t dgram_id;
/* flags is bitwise OR of zero or more of
NGTCP2_WRITE_DATAGRAM_FLAG_*. */
uint32_t flags;
@@ -647,8 +781,7 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
* NGTCP2_ERR_CALLBACK_FAILURE
* User-defined callback function failed.
*/
-int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
- uint64_t app_error_code);
+int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm);
/*
* ngtcp2_conn_close_stream closes stream |strm| if no further
@@ -664,8 +797,7 @@ int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
* NGTCP2_ERR_CALLBACK_FAILURE
* User-defined callback function failed.
*/
-int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm,
- uint64_t app_error_code);
+int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm);
/*
* ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest
@@ -673,12 +805,20 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm,
* ack_delay included in ACK frame. |ack_delay| is actually tainted
* (sent by peer), so don't assume that |ack_delay| is always smaller
* than, or equals to |rtt|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * RTT sample is ignored.
*/
-void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
- ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
+int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+ ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+
/*
* ngtcp2_conn_detect_lost_pkt detects lost packets.
*
@@ -721,16 +861,17 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm);
ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn);
ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
- ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_vmsg *vmsg,
- ngtcp2_tstamp ts);
+ int pkt_info_version, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts);
/*
- * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr|
- * frame only in the buffer pointed by |dest| whose length if
+ * ngtcp2_conn_write_single_frame_pkt writes a packet which contains
+ * |fr| frame only in the buffer pointed by |dest| whose length if
* |destlen|. |type| is a long packet type to send. If |type| is 0,
* Short packet is used. |dcid| is used as a destination connection
- * ID.
+ * ID. |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*. Only
+ * NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized.
*
* The packet written by this function will not be retransmitted.
*
@@ -742,8 +883,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
*/
ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
- uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags,
- const ngtcp2_path *path, ngtcp2_tstamp ts);
+ uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr,
+ uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts);
/*
* ngtcp2_conn_commit_local_transport_params commits the local
@@ -819,4 +960,156 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
*/
ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn);
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_idle_expiry` returns the time when a connection
+ * should be closed if it continues to be idle. If idle timeout is
+ * disabled, this function returns ``UINT64_MAX``.
+ */
+ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn);
+
+ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns);
+
+/*
+ * ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq|
+ * of unacknowledged retiring Destination Connection ID.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONNECTION_ID_LIMIT
+ * The number of unacknowledged retirement exceeds the limit.
+ */
+int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
+
+/*
+ * ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number
+ * |seq| of unacknowledged retiring Destination Connection ID. It is
+ * fine if such sequence number is not found.
+ */
+void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
+
+/*
+ * ngtcp2_conn_server_negotiate_version negotiates QUIC version. It
+ * is compatible version negotiation. It returns the negotiated QUIC
+ * version. This function must not be called by client.
+ */
+uint32_t
+ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
+ const ngtcp2_version_info *version_info);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_connection_close_pkt` writes a packet which
+ * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed
+ * by |dest| whose capacity is |datalen|.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long. The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds. The metadata includes ECN markings.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close. We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
+ ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_application_close_pkt` writes a packet which
+ * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed
+ * by |dest| whose capacity is |datalen|.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long. The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds. The metadata includes ECN markings.
+ *
+ * If handshake has not been confirmed yet, CONNECTION_CLOSE (type
+ * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written
+ * instead.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close. We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
+ ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, uint64_t app_error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts);
+
+int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn);
+
+void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_remote_transport_params` sets transport parameter
+ * |params| from a remote endpoint to |conn|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
+ * Failed to validate a remote transport parameters.
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
+ * Version negotiation failure.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+int ngtcp2_conn_set_remote_transport_params(
+ ngtcp2_conn *conn, const ngtcp2_transport_params *params);
+
#endif /* NGTCP2_CONN_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c
index 9064218dacb..dcf72e4d0ec 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c
@@ -29,6 +29,7 @@
#include "ngtcp2_str.h"
#include "ngtcp2_pkt.h"
+#include "ngtcp2_net.h"
uint64_t ngtcp2_get_uint64(const uint8_t *p) {
uint64_t n;
@@ -155,13 +156,13 @@ uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) {
return rv;
}
-uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) {
+uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n) {
uint8_t *rv;
- assert(n < 16384);
+ assert(n < 1073741824);
- rv = ngtcp2_put_uint16be(p, n);
- *p |= 0x40;
+ rv = ngtcp2_put_uint32be(p, n);
+ *p |= 0x80;
return rv;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h
index dcff0fdb8c1..99746fdb4ce 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h
@@ -29,107 +29,8 @@
# include <config.h>
#endif /* HAVE_CONFIG_H */
-#ifdef HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif /* HAVE_ARPA_INET_H */
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif /* HAVE_NETINET_IN_H */
-
-#ifdef HAVE_BYTESWAP_H
-# include <byteswap.h>
-#endif /* HAVE_BYTESWAP_H */
-
-#ifdef HAVE_ENDIAN_H
-# include <endian.h>
-#endif /* HAVE_ENDIAN_H */
-
-#ifdef HAVE_SYS_ENDIAN_H
-# include <sys/endian.h>
-#endif /* HAVE_SYS_ENDIAN_H */
-
#include <ngtcp2/ngtcp2.h>
-#if defined(HAVE_BSWAP_64) || \
- (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
-# define ngtcp2_bswap64 bswap_64
-#else /* !HAVE_BSWAP_64 */
-# define ngtcp2_bswap64(N) \
- ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \
- ngtcp2_ntohl((uint32_t)((N) >> 32)))
-#endif /* !HAVE_BSWAP_64 */
-
-#if defined(HAVE_BE64TOH) || \
- (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
-# define ngtcp2_ntohl64(N) be64toh(N)
-# define ngtcp2_htonl64(N) htobe64(N)
-#else /* !HAVE_BE64TOH */
-# if defined(WORDS_BIGENDIAN)
-# define ngtcp2_ntohl64(N) (N)
-# define ngtcp2_htonl64(N) (N)
-# else /* !WORDS_BIGENDIAN */
-# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N)
-# define ngtcp2_htonl64(N) ngtcp2_bswap64(N)
-# endif /* !WORDS_BIGENDIAN */
-#endif /* !HAVE_BE64TOH */
-
-#if defined(WIN32)
-/* Windows requires ws2_32 library for ntonl family functions. We
- define inline functions for those function so that we don't have
- dependeny on that lib. */
-
-# ifdef _MSC_VER
-# define STIN static __inline
-# else
-# define STIN static inline
-# endif
-
-STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
- uint32_t res;
- unsigned char *p = (unsigned char *)&res;
- *p++ = hostlong >> 24;
- *p++ = (hostlong >> 16) & 0xffu;
- *p++ = (hostlong >> 8) & 0xffu;
- *p = hostlong & 0xffu;
- return res;
-}
-
-STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
- uint16_t res;
- unsigned char *p = (unsigned char *)&res;
- *p++ = hostshort >> 8;
- *p = hostshort & 0xffu;
- return res;
-}
-
-STIN uint32_t ngtcp2_ntohl(uint32_t netlong) {
- uint32_t res;
- unsigned char *p = (unsigned char *)&netlong;
- res = *p++ << 24;
- res += *p++ << 16;
- res += *p++ << 8;
- res += *p;
- return res;
-}
-
-STIN uint16_t ngtcp2_ntohs(uint16_t netshort) {
- uint16_t res;
- unsigned char *p = (unsigned char *)&netshort;
- res = *p++ << 8;
- res += *p;
- return res;
-}
-
-#else /* !WIN32 */
-
-# define ngtcp2_htonl htonl
-# define ngtcp2_htons htons
-# define ngtcp2_ntohl ntohl
-# define ngtcp2_ntohs ntohs
-
-#endif /* !WIN32 */
-
/*
* ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned
* integer encoded as network byte order, and returns it in host byte
@@ -220,12 +121,12 @@ uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n);
uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n);
/*
- * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer
- * encoding. |n| must be strictly less than 16384. The function
- * always encodes |n| in 2 bytes. It returns the one beyond of the
+ * ngtcp2_put_varint30 writes |n| in |p| using variable-length integer
+ * encoding. |n| must be strictly less than 1073741824. The function
+ * always encodes |n| in 4 bytes. It returns the one beyond of the
* last written position.
*/
-uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n);
+uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n);
/*
* ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c
index e11287fd4ad..f7592f885b4 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c
@@ -30,6 +30,7 @@
#include "ngtcp2_str.h"
#include "ngtcp2_conv.h"
#include "ngtcp2_conn.h"
+#include "ngtcp2_net.h"
int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret,
size_t secretlen,
@@ -91,6 +92,8 @@ void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
size_t i;
uint64_t n;
+ assert(ivlen >= 8);
+
memcpy(dest, iv, ivlen);
n = ngtcp2_htonl64((uint64_t)pkt_num);
@@ -146,14 +149,17 @@ static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id,
return p;
}
-ngtcp2_ssize
-ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
- ngtcp2_transport_params_type exttype,
- const ngtcp2_transport_params *params) {
+static const uint8_t empty_address[16];
+
+ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
+ uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype,
+ int transport_params_version, const ngtcp2_transport_params *params) {
uint8_t *p;
size_t len = 0;
/* For some reason, gcc 7.3.0 requires this initialization. */
size_t preferred_addrlen = 0;
+ size_t version_infolen = 0;
+ (void)transport_params_version;
switch (exttype) {
case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO:
@@ -218,7 +224,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
params->initial_max_streams_uni);
}
- if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) {
+ if (params->max_udp_payload_size !=
+ NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) {
len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
params->max_udp_payload_size);
}
@@ -249,6 +256,20 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
params->max_datagram_frame_size);
}
+ if (params->grease_quic_bit) {
+ len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) +
+ ngtcp2_put_varint_len(0);
+ }
+ if (params->version_info_present) {
+ version_infolen = sizeof(uint32_t) + params->version_info.other_versionslen;
+ len += ngtcp2_put_varint_len(
+ NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) +
+ ngtcp2_put_varint_len(version_infolen) + version_infolen;
+ }
+
+ if (dest == NULL && destlen == 0) {
+ return (ngtcp2_ssize)len;
+ }
if (destlen < len) {
return NGTCP2_ERR_NOBUF;
@@ -271,13 +292,25 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS);
p = ngtcp2_put_varint(p, preferred_addrlen);
- p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr,
- sizeof(params->preferred_address.ipv4_addr));
- p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port);
+ if (params->preferred_address.ipv4_present) {
+ p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr,
+ sizeof(params->preferred_address.ipv4_addr));
+ p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port);
+ } else {
+ p = ngtcp2_cpymem(p, empty_address,
+ sizeof(params->preferred_address.ipv4_addr));
+ p = ngtcp2_put_uint16be(p, 0);
+ }
- p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr,
- sizeof(params->preferred_address.ipv6_addr));
- p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port);
+ if (params->preferred_address.ipv6_present) {
+ p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr,
+ sizeof(params->preferred_address.ipv6_addr));
+ p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port);
+ } else {
+ p = ngtcp2_cpymem(p, empty_address,
+ sizeof(params->preferred_address.ipv6_addr));
+ p = ngtcp2_put_uint16be(p, 0);
+ }
*p++ = (uint8_t)params->preferred_address.cid.datalen;
if (params->preferred_address.cid.datalen) {
@@ -330,7 +363,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
params->initial_max_streams_uni);
}
- if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) {
+ if (params->max_udp_payload_size !=
+ NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) {
p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
params->max_udp_payload_size);
}
@@ -367,6 +401,21 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
params->max_datagram_frame_size);
}
+ if (params->grease_quic_bit) {
+ p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT);
+ p = ngtcp2_put_varint(p, 0);
+ }
+
+ if (params->version_info_present) {
+ p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT);
+ p = ngtcp2_put_varint(p, version_infolen);
+ p = ngtcp2_put_uint32be(p, params->version_info.chosen_version);
+ if (params->version_info.other_versionslen) {
+ p = ngtcp2_cpymem(p, params->version_info.other_versions,
+ params->version_info.other_versionslen);
+ }
+ }
+
assert((size_t)(p - dest) == len);
return (ngtcp2_ssize)len;
@@ -467,9 +516,9 @@ static ngtcp2_ssize decode_cid_param(ngtcp2_cid *pdest, const uint8_t *p,
return (ngtcp2_ssize)(p - begin);
}
-int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
- ngtcp2_transport_params_type exttype,
- const uint8_t *data, size_t datalen) {
+int ngtcp2_decode_transport_params_versioned(
+ int transport_params_version, ngtcp2_transport_params *params,
+ ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen) {
const uint8_t *p, *end;
size_t len;
uint64_t param_type;
@@ -477,9 +526,12 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
ngtcp2_ssize nread;
int initial_scid_present = 0;
int original_dcid_present = 0;
+ size_t i;
+ (void)transport_params_version;
- p = data;
- end = data + datalen;
+ if (datalen == 0) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
/* Set default values */
memset(params, 0, sizeof(*params));
@@ -488,7 +540,7 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
params->initial_max_stream_data_bidi_local = 0;
params->initial_max_stream_data_bidi_remote = 0;
params->initial_max_stream_data_uni = 0;
- params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE;
+ params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
params->stateless_reset_token_present = 0;
params->preferred_address_present = 0;
@@ -502,10 +554,10 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
memset(&params->retry_scid, 0, sizeof(params->retry_scid));
memset(&params->initial_scid, 0, sizeof(params->initial_scid));
memset(&params->original_dcid, 0, sizeof(params->original_dcid));
+ params->version_info_present = 0;
- if (datalen == 0) {
- return 0;
- }
+ p = data;
+ end = data + datalen;
for (; (size_t)(end - p) >= 2;) {
nread = decode_varint(&param_type, p, end);
@@ -634,12 +686,24 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
params->preferred_address.ipv4_port = ngtcp2_get_uint16(p);
p += sizeof(uint16_t);
+ if (params->preferred_address.ipv4_port ||
+ memcmp(empty_address, params->preferred_address.ipv4_addr,
+ sizeof(params->preferred_address.ipv4_addr)) != 0) {
+ params->preferred_address.ipv4_present = 1;
+ }
+
memcpy(params->preferred_address.ipv6_addr, p,
sizeof(params->preferred_address.ipv6_addr));
p += sizeof(params->preferred_address.ipv6_addr);
params->preferred_address.ipv6_port = ngtcp2_get_uint16(p);
p += sizeof(uint16_t);
+ if (params->preferred_address.ipv6_port ||
+ memcmp(empty_address, params->preferred_address.ipv6_addr,
+ sizeof(params->preferred_address.ipv6_addr)) != 0) {
+ params->preferred_address.ipv6_present = 1;
+ }
+
/* cid */
params->preferred_address.cid.datalen = *p++;
len += params->preferred_address.cid.datalen;
@@ -720,6 +784,45 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
}
p += nread;
break;
+ case NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT:
+ nread = decode_varint(&valuelen, p, end);
+ if (nread < 0 || valuelen != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ params->grease_quic_bit = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT:
+ nread = decode_varint(&valuelen, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ if ((size_t)(end - p) < valuelen) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ params->version_info.chosen_version = ngtcp2_get_uint32(p);
+ if (params->version_info.chosen_version == 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += sizeof(uint32_t);
+ if (valuelen > sizeof(uint32_t)) {
+ params->version_info.other_versions = (uint8_t *)p;
+ params->version_info.other_versionslen =
+ (size_t)valuelen - sizeof(uint32_t);
+
+ for (i = sizeof(uint32_t); i < valuelen;
+ i += sizeof(uint32_t), p += sizeof(uint32_t)) {
+ if (ngtcp2_get_uint32(p) == 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ }
+ }
+ params->version_info_present = 1;
+ break;
default:
/* Ignore unknown parameter */
nread = decode_varint(&valuelen, p, end);
@@ -747,3 +850,76 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params,
return 0;
}
+
+static int transport_params_copy_new(ngtcp2_transport_params **pdest,
+ const ngtcp2_transport_params *src,
+ const ngtcp2_mem *mem) {
+ size_t len = sizeof(**pdest);
+ ngtcp2_transport_params *dest;
+ uint8_t *p;
+
+ if (src->version_info_present) {
+ len += src->version_info.other_versionslen;
+ }
+
+ dest = ngtcp2_mem_malloc(mem, len);
+ if (dest == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ *dest = *src;
+
+ if (src->version_info_present && src->version_info.other_versionslen) {
+ p = (uint8_t *)dest + sizeof(*dest);
+ memcpy(p, src->version_info.other_versions,
+ src->version_info.other_versionslen);
+ dest->version_info.other_versions = p;
+ }
+
+ *pdest = dest;
+
+ return 0;
+}
+
+int ngtcp2_decode_transport_params_new(ngtcp2_transport_params **pparams,
+ ngtcp2_transport_params_type exttype,
+ const uint8_t *data, size_t datalen,
+ const ngtcp2_mem *mem) {
+ int rv;
+ ngtcp2_transport_params params;
+
+ rv = ngtcp2_decode_transport_params(&params, exttype, data, datalen);
+ if (rv < 0) {
+ return rv;
+ }
+
+ if (mem == NULL) {
+ mem = ngtcp2_mem_default();
+ }
+
+ return transport_params_copy_new(pparams, &params, mem);
+}
+
+void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem) {
+ if (params == NULL) {
+ return;
+ }
+
+ if (mem == NULL) {
+ mem = ngtcp2_mem_default();
+ }
+
+ ngtcp2_mem_free(mem, params);
+}
+
+int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest,
+ const ngtcp2_transport_params *src,
+ const ngtcp2_mem *mem) {
+ if (src == NULL) {
+ *pdest = NULL;
+ return 0;
+ }
+
+ return transport_params_copy_new(pdest, src, mem);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h
index 6e6f12a0956..9a9d95f5b9f 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h
@@ -41,11 +41,38 @@
/* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */
#define NGTCP2_MAX_AEAD_OVERHEAD 16
+/* ngtcp2_transport_param_id is the registry of QUIC transport
+ parameter ID. */
+typedef enum ngtcp2_transport_param_id {
+ NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000,
+ NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001,
+ NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002,
+ NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009,
+ NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a,
+ NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b,
+ NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c,
+ NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d,
+ NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f,
+ NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010,
+ /* https://datatracker.ietf.org/doc/html/rfc9221 */
+ NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020,
+ NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2,
+ /* https://quicwg.org/quic-v2/draft-ietf-quic-v2.html */
+ NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT = 0xff73db,
+} ngtcp2_transport_param_id;
+
/* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00
+#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00u
/* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is
set. */
-#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01
+#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01u
typedef struct ngtcp2_crypto_km {
ngtcp2_vec secret;
@@ -100,4 +127,21 @@ typedef struct ngtcp2_crypto_cc {
void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
int64_t pkt_num);
+/*
+ * ngtcp2_transport_params_copy_new makes a copy of |src|, and assigns
+ * it to |*pdest|. If |src| is NULL, NULL is assigned to |*pdest|.
+ *
+ * Caller is responsible to call ngtcp2_transport_params_del to free
+ * the memory assigned to |*pdest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest,
+ const ngtcp2_transport_params *src,
+ const ngtcp2_mem *mem);
+
#endif /* NGTCP2_CRYPTO_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c
index bd15e0988be..8f676da3ef0 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c
@@ -64,8 +64,8 @@ const char *ngtcp2_strerror(int liberr) {
return "ERR_MALFORMED_TRANSPORT_PARAM";
case NGTCP2_ERR_FRAME_ENCODING:
return "ERR_FRAME_ENCODING";
- case NGTCP2_ERR_TLS_DECRYPT:
- return "ERR_TLS_DECRYPT";
+ case NGTCP2_ERR_DECRYPT:
+ return "ERR_DECRYPT";
case NGTCP2_ERR_STREAM_SHUT_WR:
return "ERR_STREAM_SHUT_WR";
case NGTCP2_ERR_STREAM_NOT_FOUND:
@@ -82,8 +82,6 @@ const char *ngtcp2_strerror(int liberr) {
return "ERR_TRANSPORT_PARAM";
case NGTCP2_ERR_DISCARD_PKT:
return "ERR_DISCARD_PKT";
- case NGTCP2_ERR_PATH_VALIDATION_FAILED:
- return "ERR_PATH_VALIDATION_FAILED";
case NGTCP2_ERR_CONN_ID_BLOCKED:
return "ERR_CONN_ID_BLOCKED";
case NGTCP2_ERR_CALLBACK_FAILURE:
@@ -102,6 +100,14 @@ const char *ngtcp2_strerror(int liberr) {
return "ERR_AEAD_LIMIT_REACHED";
case NGTCP2_ERR_NO_VIABLE_PATH:
return "ERR_NO_VIABLE_PATH";
+ case NGTCP2_ERR_VERSION_NEGOTIATION:
+ return "ERR_VERSION_NEGOTIATION";
+ case NGTCP2_ERR_HANDSHAKE_TIMEOUT:
+ return "ERR_HANDSHAKE_TIMEOUT";
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ return "ERR_VERSION_NEGOTIATION_FAILURE";
+ case NGTCP2_ERR_IDLE_CLOSE:
+ return "ERR_IDLE_CLOSE";
default:
return "(unknown)";
}
@@ -129,6 +135,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
case NGTCP2_ERR_TRANSPORT_PARAM:
return NGTCP2_TRANSPORT_PARAMETER_ERROR;
case NGTCP2_ERR_INVALID_ARGUMENT:
+ case NGTCP2_ERR_NOMEM:
+ case NGTCP2_ERR_CALLBACK_FAILURE:
return NGTCP2_INTERNAL_ERROR;
case NGTCP2_ERR_STREAM_STATE:
return NGTCP2_STREAM_STATE_ERROR;
@@ -138,6 +146,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
return NGTCP2_AEAD_LIMIT_REACHED;
case NGTCP2_ERR_NO_VIABLE_PATH:
return NGTCP2_NO_VIABLE_PATH;
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ return NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT;
default:
return NGTCP2_PROTOCOL_VIOLATION;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
index 6e7f3b7e554..87c23898e82 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
@@ -23,29 +23,26 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ngtcp2_gaptr.h"
-#include "ngtcp2_range.h"
#include <string.h>
#include <assert.h>
-int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
- int rv;
- ngtcp2_range range = {0, UINT64_MAX};
+void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
+ ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+ mem);
- rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar,
- sizeof(ngtcp2_range), mem);
- if (rv != 0) {
- return rv;
- }
+ gaptr->mem = mem;
+}
+
+static int gaptr_gap_init(ngtcp2_gaptr *gaptr) {
+ ngtcp2_range range = {0, UINT64_MAX};
+ int rv;
rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL);
if (rv != 0) {
- ngtcp2_ksl_free(&gaptr->gap);
return rv;
}
- gaptr->mem = mem;
-
return 0;
}
@@ -57,11 +54,18 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) {
ngtcp2_ksl_free(&gaptr->gap);
}
-int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
+int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) {
int rv;
ngtcp2_range k, m, l, r, q = {offset, offset + datalen};
ngtcp2_ksl_it it;
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ rv = gaptr_gap_init(gaptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
ngtcp2_ksl_range_exclusive_compar);
@@ -73,7 +77,7 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
}
if (ngtcp2_range_eq(&k, &m)) {
- ngtcp2_ksl_remove(&gaptr->gap, &it, &k);
+ ngtcp2_ksl_remove_hint(&gaptr->gap, &it, &it, &k);
continue;
}
ngtcp2_range_cut(&l, &r, &k, &m);
@@ -95,35 +99,69 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
}
uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) {
- ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap);
- ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+ ngtcp2_ksl_it it;
+ ngtcp2_range r;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = ngtcp2_ksl_begin(&gaptr->gap);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
return r.begin;
}
-ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
- uint64_t offset) {
+ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+ uint64_t offset) {
ngtcp2_range q = {offset, offset + 1};
- return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
- ngtcp2_ksl_range_exclusive_compar);
+ ngtcp2_ksl_it it;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ ngtcp2_range r = {0, UINT64_MAX};
+ return r;
+ }
+
+ it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+
+ assert(!ngtcp2_ksl_it_end(&it));
+
+ return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
}
int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
- size_t datalen) {
+ uint64_t datalen) {
ngtcp2_range q = {offset, offset + datalen};
- ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar(
- &gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar);
- ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
- ngtcp2_range m = ngtcp2_range_intersect(&q, &k);
+ ngtcp2_ksl_it it;
+ ngtcp2_range k;
+ ngtcp2_range m;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+ k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+ m = ngtcp2_range_intersect(&q, &k);
+
return ngtcp2_range_len(&m) == 0;
}
void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) {
- ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap);
+ ngtcp2_ksl_it it;
ngtcp2_range r;
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ return;
+ }
+
+ it = ngtcp2_ksl_begin(&gaptr->gap);
+
assert(!ngtcp2_ksl_it_end(&it));
r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
- ngtcp2_ksl_remove(&gaptr->gap, NULL, &r);
+ ngtcp2_ksl_remove_hint(&gaptr->gap, NULL, &it, &r);
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
index 500d376008a..0f100a81c42 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
@@ -33,6 +33,7 @@
#include "ngtcp2_mem.h"
#include "ngtcp2_ksl.h"
+#include "ngtcp2_range.h"
/*
* ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX).
@@ -47,14 +48,8 @@ typedef struct ngtcp2_gaptr {
/*
* ngtcp2_gaptr_init initializes |gaptr|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
*/
-int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem);
+void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem);
/*
* ngtcp2_gaptr_free frees resources allocated for |gaptr|.
@@ -71,7 +66,7 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr);
* NGTCP2_ERR_NOMEM
* Out of memory
*/
-int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen);
+int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen);
/*
* ngtcp2_gaptr_first_gap_offset returns the offset to the first gap.
@@ -80,18 +75,18 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen);
uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr);
/*
- * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to
- * the first gap which overlaps or comes after |offset|.
+ * ngtcp2_gaptr_get_first_gap_after returns the first gap which
+ * overlaps or comes after |offset|.
*/
-ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
- uint64_t offset);
+ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+ uint64_t offset);
/*
* ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset +
* datalen) is completely pushed into this object.
*/
int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
- size_t datalen);
+ uint64_t datalen);
/*
* ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c
index f04806b4a8b..d9880227690 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c
@@ -26,17 +26,10 @@
#include <assert.h>
-int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) {
- int rv;
-
- rv = ngtcp2_gaptr_init(&idtr->gap, mem);
- if (rv != 0) {
- return rv;
- }
+void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) {
+ ngtcp2_gaptr_init(&idtr->gap, mem);
idtr->server = server;
-
- return 0;
}
void ngtcp2_idtr_free(ngtcp2_idtr *idtr) {
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h
index 1be64dc16e7..edb8c68c8db 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h
@@ -51,14 +51,8 @@ typedef struct ngtcp2_idtr {
*
* If this object records server initiated ID (even number), set
* |server| to nonzero.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
*/
-int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
+void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
/*
* ngtcp2_idtr_free frees resources allocated for |idtr|.
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c
index fd25e3514e8..0bd424cb0bc 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c
@@ -33,9 +33,11 @@
#include "ngtcp2_mem.h"
#include "ngtcp2_range.h"
+static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
+
static size_t ksl_nodelen(size_t keylen) {
- return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
- (size_t)~0xf;
+ return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) &
+ ~(uintptr_t)0xfu;
}
static size_t ksl_blklen(size_t nodelen) {
@@ -51,31 +53,48 @@ static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node,
memcpy(node->key, key, ksl->keylen);
}
-int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
- const ngtcp2_mem *mem) {
+void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+ const ngtcp2_mem *mem) {
size_t nodelen = ksl_nodelen(keylen);
- size_t blklen = ksl_blklen(nodelen);
- ngtcp2_ksl_blk *head;
- ksl->head = ngtcp2_mem_malloc(mem, blklen);
- if (!ksl->head) {
- return NGTCP2_ERR_NOMEM;
- }
- ksl->front = ksl->back = ksl->head;
+ ngtcp2_objalloc_init(&ksl->blkalloc,
+ ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8,
+ mem);
+
+ ksl->head = NULL;
+ ksl->front = ksl->back = NULL;
ksl->compar = compar;
ksl->keylen = keylen;
ksl->nodelen = nodelen;
ksl->n = 0;
- ksl->mem = mem;
+}
+
+static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) {
+ return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc,
+ ksl_blklen(ksl->nodelen));
+}
+
+static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
+ ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
+}
+
+static int ksl_head_init(ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl);
+ if (!head) {
+ return NGTCP2_ERR_NOMEM;
+ }
- head = ksl->head;
head->next = head->prev = NULL;
head->n = 0;
head->leaf = 1;
+ ksl->head = head;
+ ksl->front = ksl->back = head;
+
return 0;
}
+#ifdef NOMEMPOOL
/*
* ksl_free_blk frees |blk| recursively.
*/
@@ -88,15 +107,20 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
}
}
- ngtcp2_mem_free(ksl->mem, blk);
+ ksl_blk_objalloc_del(ksl, blk);
}
+#endif /* NOMEMPOOL */
void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
- if (!ksl) {
+ if (!ksl || !ksl->head) {
return;
}
+#ifdef NOMEMPOOL
ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ ngtcp2_objalloc_free(&ksl->blkalloc);
}
/*
@@ -110,7 +134,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
ngtcp2_ksl_blk *rblk;
- rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+ rblk = ksl_blk_objalloc_new(ksl);
if (rblk == NULL) {
return NULL;
}
@@ -194,9 +218,9 @@ static int ksl_split_head(ngtcp2_ksl *ksl) {
lblk = ksl->head;
- nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+ nhead = ksl_blk_objalloc_new(ksl);
if (nhead == NULL) {
- ngtcp2_mem_free(ksl->mem, rblk);
+ ksl_blk_objalloc_del(ksl, rblk);
return NGTCP2_ERR_NOMEM;
}
nhead->next = nhead->prev = NULL;
@@ -240,29 +264,33 @@ static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i,
static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) {
- ngtcp2_ssize left = -1, right = (ngtcp2_ssize)blk->n, mid;
+ size_t i;
ngtcp2_ksl_node *node;
- while (right - left > 1) {
- mid = (left + right) >> 1;
- node = ngtcp2_ksl_nth_node(ksl, blk, (size_t)mid);
- if (compar((ngtcp2_ksl_key *)node->key, key)) {
- left = mid;
- } else {
- right = mid;
- }
- }
+ for (i = 0, node = (ngtcp2_ksl_node *)(void *)blk->nodes;
+ i < blk->n && compar((ngtcp2_ksl_key *)node->key, key);
+ ++i, node = (ngtcp2_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
+ ;
- return (size_t)right;
+ return i;
}
int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
const ngtcp2_ksl_key *key, void *data) {
- ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_blk *blk;
ngtcp2_ksl_node *node;
size_t i;
int rv;
+ if (!ksl->head) {
+ rv = ksl_head_init(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ blk = ksl->head;
+
if (blk->n == NGTCP2_KSL_MAX_NBLK) {
rv = ksl_split_head(ksl);
if (rv != 0) {
@@ -374,10 +402,10 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
ksl->back = lblk;
}
- ngtcp2_mem_free(ksl->mem, rblk);
+ ksl_blk_objalloc_del(ksl, rblk);
if (ksl->head == blk && blk->n == 2) {
- ngtcp2_mem_free(ksl->mem, ksl->head);
+ ksl_blk_objalloc_del(ksl, ksl->head);
ksl->head = lblk;
} else {
ksl_remove_node(ksl, blk, i + 1);
@@ -469,12 +497,42 @@ static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs,
return !compar(lhs, rhs) && !compar(rhs, lhs);
}
+int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_it *hint,
+ const ngtcp2_ksl_key *key) {
+ ngtcp2_ksl_blk *blk = hint->blk;
+
+ assert(ksl->head);
+
+ if (blk->n <= NGTCP2_KSL_MIN_NBLK) {
+ return ngtcp2_ksl_remove(ksl, it, key);
+ }
+
+ ksl_remove_node(ksl, blk, hint->i);
+
+ --ksl->n;
+
+ if (it) {
+ if (hint->i == blk->n && blk->next) {
+ ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ ngtcp2_ksl_it_init(it, ksl, blk, hint->i);
+ }
+ }
+
+ return 0;
+}
+
int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
const ngtcp2_ksl_key *key) {
ngtcp2_ksl_blk *blk = ksl->head;
ngtcp2_ksl_node *node;
size_t i;
+ if (!ksl->head) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
if (!blk->leaf && blk->n == 2 &&
ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK &&
ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) {
@@ -550,6 +608,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
ngtcp2_ksl_it it;
size_t i;
+ if (!blk) {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
for (;;) {
i = ksl_bsearch(ksl, blk, key, ksl->compar);
@@ -587,6 +650,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
ngtcp2_ksl_it it;
size_t i;
+ if (!blk) {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
for (;;) {
i = ksl_bsearch(ksl, blk, key, compar);
@@ -623,6 +691,8 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
ngtcp2_ksl_node *node;
size_t i;
+ assert(ksl->head);
+
for (;;) {
i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
@@ -667,36 +737,49 @@ static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) {
size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; }
void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
- size_t i;
- ngtcp2_ksl_blk *head;
-
- if (!ksl->head->leaf) {
- for (i = 0; i < ksl->head->n; ++i) {
- ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk);
- }
+ if (!ksl->head) {
+ return;
}
- ksl->front = ksl->back = ksl->head;
- ksl->n = 0;
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
- head = ksl->head;
+ ksl->front = ksl->back = ksl->head = NULL;
+ ksl->n = 0;
- head->next = head->prev = NULL;
- head->n = 0;
- head->leaf = 1;
+ ngtcp2_objalloc_clear(&ksl->blkalloc);
}
-void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); }
+void ngtcp2_ksl_print(ngtcp2_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+ ksl_print(ksl, ksl->head, 0);
+}
ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) {
ngtcp2_ksl_it it;
- ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
+
+ if (ksl->head) {
+ ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
+ } else {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
return it;
}
ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) {
ngtcp2_ksl_it it;
- ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+
+ if (ksl->head) {
+ ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+ } else {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
return it;
}
@@ -707,11 +790,6 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
it->i = i;
}
-void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) {
- assert(it->i < it->blk->n);
- return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data;
-}
-
void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) {
assert(!ngtcp2_ksl_it_begin(it));
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h
index f24487d03e9..312a151d4aa 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h
@@ -33,6 +33,8 @@
#include <ngtcp2/ngtcp2.h>
+#include "ngtcp2_objalloc.h"
+
/*
* Skip List using single key instead of range.
*/
@@ -79,25 +81,33 @@ struct ngtcp2_ksl_node {
* ngtcp2_ksl_blk contains ngtcp2_ksl_node objects.
*/
struct ngtcp2_ksl_blk {
- /* next points to the next block if leaf field is nonzero. */
- ngtcp2_ksl_blk *next;
- /* prev points to the previous block if leaf field is nonzero. */
- ngtcp2_ksl_blk *prev;
- /* n is the number of nodes this object contains in nodes. */
- uint32_t n;
- /* leaf is nonzero if this block contains leaf nodes. */
- uint32_t leaf;
union {
- uint64_t align;
- /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
- ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is
- allocated along with the additional variable length key
- storage, the size of buffer is unknown until ngtcp2_ksl_init is
- called. */
- uint8_t nodes[1];
+ struct {
+ /* next points to the next block if leaf field is nonzero. */
+ ngtcp2_ksl_blk *next;
+ /* prev points to the previous block if leaf field is nonzero. */
+ ngtcp2_ksl_blk *prev;
+ /* n is the number of nodes this object contains in nodes. */
+ uint32_t n;
+ /* leaf is nonzero if this block contains leaf nodes. */
+ uint32_t leaf;
+ union {
+ uint64_t align;
+ /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
+ ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is
+ allocated along with the additional variable length key
+ storage, the size of buffer is unknown until ngtcp2_ksl_init is
+ called. */
+ uint8_t nodes[1];
+ };
+ };
+
+ ngtcp2_opl_entry oplent;
};
};
+ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent);
+
/*
* ngtcp2_ksl_compar is a function type which returns nonzero if key
* |lhs| should be placed before |rhs|. It returns 0 otherwise.
@@ -122,6 +132,7 @@ struct ngtcp2_ksl_it {
* ngtcp2_ksl is a deterministic paged skip list.
*/
struct ngtcp2_ksl {
+ ngtcp2_objalloc blkalloc;
/* head points to the root block. */
ngtcp2_ksl_blk *head;
/* front points to the first leaf block. */
@@ -135,21 +146,14 @@ struct ngtcp2_ksl {
/* nodelen is the actual size of ngtcp2_ksl_node including key
storage. */
size_t nodelen;
- const ngtcp2_mem *mem;
};
/*
* ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare
* function. |keylen| is the length of key.
- *
- * It returns 0 if it succeeds, or one of the following negative error
- * codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
*/
-int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
- const ngtcp2_mem *mem);
+void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+ const ngtcp2_mem *mem);
/*
* ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is
@@ -192,6 +196,17 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
const ngtcp2_ksl_key *key);
/*
+ * ngtcp2_ksl_remove_hint removes the |key| from |ksl|. |hint| must
+ * point to the same node denoted by |key|. |hint| is used to remove
+ * a node efficiently in some cases. Other than that, it behaves
+ * exactly like ngtcp2_ksl_remove. |it| and |hint| can point to the
+ * same object.
+ */
+int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_it *hint,
+ const ngtcp2_ksl_key *key);
+
+/*
* ngtcp2_ksl_lower_bound returns the iterator which points to the
* first node which has the key which is equal to |key| or the last
* node which satisfies !compar(&node->key, key). If there is no such
@@ -266,7 +281,8 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
* |it| points to. It is undefined to call this function when
* ngtcp2_ksl_it_end(it) returns nonzero.
*/
-void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it);
+#define ngtcp2_ksl_it_get(IT) \
+ ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
/*
* ngtcp2_ksl_it_next advances the iterator by one. It is undefined
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c
index 0259404d3e2..ee37ff3517b 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c
@@ -34,6 +34,7 @@
#include "ngtcp2_str.h"
#include "ngtcp2_vec.h"
#include "ngtcp2_macro.h"
+#include "ngtcp2_conv.h"
void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
ngtcp2_printf log_printf, ngtcp2_tstamp ts,
@@ -69,7 +70,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
*
* # Frame event
*
- * <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>)
+ * <DIR> <PKN> <PKTNAME> <FRAMENAME>(<FRAMETYPE>)
*
* <DIR>:
* Flow direction. tx=transmission, rx=reception
@@ -78,10 +79,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
* Packet number.
*
* <PKTNAME>:
- * Packet name. (e.g., Initial, Handshake, S01)
- *
- * <PKTTYPE>:
- * Packet type in hex string.
+ * Packet name. (e.g., Initial, Handshake, 1RTT)
*
* <FRAMENAME>:
* Frame name. (e.g., STREAM, ACK, PING)
@@ -94,16 +92,16 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
/* TODO Split second and remaining fraction with comma */
#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s"
-#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)"
+#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s"
#define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters"
#define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \
timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \
- (DIR), hd->pkt_num, strpkttype(hd), hd->type
+ (DIR), hd->pkt_num, strpkttype(hd)
#define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \
timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \
- (DIR), hd->pkt_num, strpkttype(hd), hd->type
+ (DIR), hd->pkt_num, strpkttype(hd)
#define NGTCP2_LOG_TP_HD_FIELDS \
timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry"
@@ -140,6 +138,8 @@ static const char *strerrorcode(uint64_t error_code) {
return "CRYPTO_BUFFER_EXCEEDED";
case NGTCP2_KEY_UPDATE_ERROR:
return "KEY_UPDATE_ERROR";
+ case NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT:
+ return "VERSION_NEGOTIATION_ERROR";
default:
if (0x100u <= error_code && error_code <= 0x1ffu) {
return "CRYPTO_ERROR";
@@ -155,8 +155,6 @@ static const char *strapperrorcode(uint64_t app_error_code) {
static const char *strpkttype_long(uint8_t type) {
switch (type) {
- case NGTCP2_PKT_VERSION_NEGOTIATION:
- return "VN";
case NGTCP2_PKT_INITIAL:
return "Initial";
case NGTCP2_PKT_RETRY:
@@ -174,7 +172,26 @@ static const char *strpkttype(const ngtcp2_pkt_hd *hd) {
if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
return strpkttype_long(hd->type);
}
- return "Short";
+
+ switch (hd->type) {
+ case NGTCP2_PKT_VERSION_NEGOTIATION:
+ return "VN";
+ case NGTCP2_PKT_STATELESS_RESET:
+ return "SR";
+ case NGTCP2_PKT_1RTT:
+ return "1RTT";
+ default:
+ return "(unknown)";
+ }
+}
+
+static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) {
+ ngtcp2_pkt_hd hd = {0};
+
+ hd.type = type;
+ hd.flags = flags;
+
+ return strpkttype(&hd);
}
static const char *strevent(ngtcp2_log_event ev) {
@@ -201,13 +218,13 @@ static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; }
static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_stream *fr, const char *dir) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64
- " fin=%d offset=%" PRIu64 " len=%zu uni=%d"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags,
- fr->stream_id, fr->fin, fr->offset,
- ngtcp2_vec_len(fr->data, fr->datacnt),
- (fr->stream_id & 0x2) != 0);
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 " fin=%d offset=%" PRIu64
+ " len=%" PRIu64 " uni=%d"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id,
+ fr->fin, fr->offset, ngtcp2_vec_len(fr->data, fr->datacnt),
+ (fr->stream_id & 0x2) != 0);
}
static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
@@ -396,17 +413,11 @@ static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_crypto *fr, const char *dir) {
- size_t datalen = 0;
- size_t i;
-
- for (i = 0; i < fr->datacnt; ++i) {
- datalen += fr->data[i].len;
- }
-
log->log_printf(
log->user_data,
(NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen);
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset,
+ ngtcp2_vec_len(fr->data, fr->datacnt));
}
static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
@@ -446,7 +457,8 @@ static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_datagram *fr, const char *dir) {
- log->log_printf(log->user_data, (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%zu"),
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%" PRIu64),
NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
ngtcp2_vec_len(fr->data, fr->datacnt));
}
@@ -570,6 +582,8 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) {
memset(&shd, 0, sizeof(shd));
+ shd.type = NGTCP2_PKT_STATELESS_RESET;
+
log->log_printf(
log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"),
NGTCP2_LOG_PKT_HD_FIELDS("rx"),
@@ -583,6 +597,7 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1];
uint8_t addr[16 * 2 + 7 + 1];
uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1];
+ size_t i;
if (!log->log_printf) {
return;
@@ -694,6 +709,26 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
log->log_printf(log->user_data,
(NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64),
NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit);
+
+ if (params->version_info_present) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version);
+
+ assert(!(params->version_info.other_versionslen & 0x3));
+
+ for (i = 0; i < params->version_info.other_versionslen;
+ i += sizeof(uint32_t)) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " version_information.other_versions[%zu]=0x%08x"),
+ NGTCP2_LOG_TP_HD_FIELDS, i >> 2,
+ ngtcp2_get_uint32(&params->version_info.other_versions[i]));
+ }
+ }
}
void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
@@ -702,11 +737,9 @@ void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
return;
}
- ngtcp2_log_info(
- log, NGTCP2_LOG_EVENT_RCV,
- "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num,
- (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short",
- type, sent_ts);
+ ngtcp2_log_info(log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num,
+ strpkttype_type_flags(type, flags), sent_ts);
}
static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
@@ -718,15 +751,21 @@ static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
return;
}
- ngtcp2_log_info(
- log, NGTCP2_LOG_EVENT_PKT,
- "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d",
- dir, hd->pkt_num,
- (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
- (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen),
- (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type)
- : "Short",
- hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
+ if (hd->type == NGTCP2_PKT_1RTT) {
+ ngtcp2_log_info(
+ log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d",
+ dir, hd->pkt_num,
+ (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
+ strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
+ } else {
+ ngtcp2_log_info(
+ log, NGTCP2_LOG_EVENT_PKT,
+ "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu",
+ dir, hd->pkt_num,
+ (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
+ (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen),
+ hd->version, strpkttype(hd), hd->len);
+ }
}
void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
@@ -762,6 +801,6 @@ void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT,
- "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num,
- strpkttype(hd), hd->type);
+ "cancel tx pkn=%" PRId64 " type=%s", hd->pkt_num,
+ strpkttype(hd));
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h
index bd1ac240a93..029ef1b757a 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h
@@ -33,9 +33,7 @@
#include "ngtcp2_pkt.h"
-typedef struct ngtcp2_log ngtcp2_log;
-
-struct ngtcp2_log {
+typedef struct ngtcp2_log {
/* log_printf is a sink to write log. NULL means no logging
output. */
ngtcp2_printf log_printf;
@@ -49,7 +47,44 @@ struct ngtcp2_log {
void *user_data;
/* scid is SCID encoded as NULL-terminated hex string. */
uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1];
-};
+} ngtcp2_log;
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_log_event` defines an event of ngtcp2 library
+ * internal logger.
+ */
+typedef enum ngtcp2_log_event {
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event.
+ */
+ NGTCP2_LOG_EVENT_NONE,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event
+ */
+ NGTCP2_LOG_EVENT_CON,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event.
+ */
+ NGTCP2_LOG_EVENT_PKT,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event.
+ */
+ NGTCP2_LOG_EVENT_FRM,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event.
+ */
+ NGTCP2_LOG_EVENT_RCV,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event.
+ */
+ NGTCP2_LOG_EVENT_CRY,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event.
+ */
+ NGTCP2_LOG_EVENT_PTV,
+} ngtcp2_log_event;
void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
ngtcp2_printf log_printf, ngtcp2_tstamp ts,
@@ -77,4 +112,12 @@ void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
+/**
+ * @function
+ *
+ * `ngtcp2_log_info` writes info level log.
+ */
+void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
+ ...);
+
#endif /* NGTCP2_LOG_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c
index 5d24961dc98..12bc6e84bd4 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c
@@ -27,170 +27,166 @@
#include <string.h>
#include <assert.h>
+#include <stdio.h>
#include "ngtcp2_conv.h"
-#define INITIAL_TABLE_LENGTH 256
+#define NGTCP2_INITIAL_TABLE_LENBITS 4
-int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
+void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
map->mem = mem;
- map->tablelen = INITIAL_TABLE_LENGTH;
- map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket));
- if (map->table == NULL) {
- return NGTCP2_ERR_NOMEM;
- }
-
+ map->tablelen = 0;
+ map->tablelenbits = 0;
+ map->table = NULL;
map->size = 0;
-
- return 0;
}
void ngtcp2_map_free(ngtcp2_map *map) {
- size_t i;
- ngtcp2_map_bucket *bkt;
-
if (!map) {
return;
}
- for (i = 0; i < map->tablelen; ++i) {
- bkt = &map->table[i];
- if (bkt->ksl) {
- ngtcp2_ksl_free(bkt->ksl);
- ngtcp2_mem_free(map->mem, bkt->ksl);
- }
- }
-
ngtcp2_mem_free(map->mem, map->table);
}
-void ngtcp2_map_each_free(ngtcp2_map *map,
- int (*func)(ngtcp2_map_entry *entry, void *ptr),
+void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
uint32_t i;
ngtcp2_map_bucket *bkt;
- ngtcp2_ksl_it it;
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
- if (bkt->ptr) {
- func(bkt->ptr, ptr);
- bkt->ptr = NULL;
- assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0);
+ if (bkt->data == NULL) {
continue;
}
- if (bkt->ksl) {
- for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it);
- ngtcp2_ksl_it_next(&it)) {
- func(ngtcp2_ksl_it_get(&it), ptr);
- }
-
- ngtcp2_ksl_free(bkt->ksl);
- ngtcp2_mem_free(map->mem, bkt->ksl);
- bkt->ksl = NULL;
- }
+ func(bkt->data, ptr);
}
}
-int ngtcp2_map_each(ngtcp2_map *map,
- int (*func)(ngtcp2_map_entry *entry, void *ptr),
+int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
int rv;
uint32_t i;
ngtcp2_map_bucket *bkt;
- ngtcp2_ksl_it it;
+
+ if (map->size == 0) {
+ return 0;
+ }
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
- if (bkt->ptr) {
- rv = func(bkt->ptr, ptr);
- if (rv != 0) {
- return rv;
- }
- assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0);
+ if (bkt->data == NULL) {
continue;
}
- if (bkt->ksl) {
- for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it);
- ngtcp2_ksl_it_next(&it)) {
- rv = func(ngtcp2_ksl_it_get(&it), ptr);
- if (rv != 0) {
- return rv;
- }
- }
+ rv = func(bkt->data, ptr);
+ if (rv != 0) {
+ return rv;
}
}
+
return 0;
}
-void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) {
- entry->key = key;
+static uint32_t hash(ngtcp2_map_key_type key) {
+ return (uint32_t)((key * 11400714819323198485llu) >> 32);
}
-/* FNV1a hash */
-static uint32_t hash(key_type key, uint32_t mod) {
- uint8_t *p, *end;
- uint32_t h = 0x811C9DC5u;
+static size_t h2idx(uint32_t hash, uint32_t bits) {
+ return hash >> (32 - bits);
+}
- key = ngtcp2_htonl64(key);
- p = (uint8_t *)&key;
- end = p + sizeof(key_type);
+static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
+ ngtcp2_map_bucket *bkt, size_t idx) {
+ return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
+}
- for (; p != end;) {
- h ^= *p++;
- h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
- }
+static void map_bucket_swap(ngtcp2_map_bucket *bkt, uint32_t *phash,
+ ngtcp2_map_key_type *pkey, void **pdata) {
+ uint32_t h = bkt->hash;
+ ngtcp2_map_key_type key = bkt->key;
+ void *data = bkt->data;
- return h & (mod - 1);
+ bkt->hash = *phash;
+ bkt->key = *pkey;
+ bkt->data = *pdata;
+
+ *phash = h;
+ *pkey = key;
+ *pdata = data;
}
-static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
- return *(key_type *)lhs < *(key_type *)rhs;
+static void map_bucket_set_data(ngtcp2_map_bucket *bkt, uint32_t hash,
+ ngtcp2_map_key_type key, void *data) {
+ bkt->hash = hash;
+ bkt->key = key;
+ bkt->data = data;
}
-static int map_insert(ngtcp2_map *map, ngtcp2_map_bucket *table,
- uint32_t tablelen, ngtcp2_map_entry *entry) {
- uint32_t h = hash(entry->key, tablelen);
- ngtcp2_map_bucket *bkt = &table[h];
- const ngtcp2_mem *mem = map->mem;
- int rv;
+void ngtcp2_map_print_distance(ngtcp2_map *map) {
+ uint32_t i;
+ size_t idx;
+ ngtcp2_map_bucket *bkt;
- if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) {
- bkt->ptr = entry;
- return 0;
- }
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
- if (!bkt->ksl) {
- bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl));
- if (bkt->ksl == NULL) {
- return NGTCP2_ERR_NOMEM;
+ if (bkt->data == NULL) {
+ fprintf(stderr, "@%u <EMPTY>\n", i);
+ continue;
}
- ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
+
+ idx = h2idx(bkt->hash, map->tablelenbits);
+ fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i,
+ bkt->hash, bkt->key, idx,
+ distance(map->tablelen, map->tablelenbits, bkt, idx));
}
+}
- if (bkt->ptr) {
- rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
- if (rv != 0) {
- return rv;
+static int insert(ngtcp2_map_bucket *table, uint32_t tablelen,
+ uint32_t tablelenbits, uint32_t hash, ngtcp2_map_key_type key,
+ void *data) {
+ size_t idx = h2idx(hash, tablelenbits);
+ size_t d = 0, dd;
+ ngtcp2_map_bucket *bkt;
+
+ for (;;) {
+ bkt = &table[idx];
+
+ if (bkt->data == NULL) {
+ map_bucket_set_data(bkt, hash, key, data);
+ return 0;
}
- bkt->ptr = NULL;
- }
+ dd = distance(tablelen, tablelenbits, bkt, idx);
+ if (d > dd) {
+ map_bucket_swap(bkt, &hash, &key, &data);
+ d = dd;
+ } else if (bkt->key == key) {
+ /* TODO This check is just a waste after first swap or if this
+ function is called from map_resize. That said, there is no
+ difference with or without this conditional in performance
+ wise. */
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
- return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
+ ++d;
+ idx = (idx + 1) & (tablelen - 1);
+ }
}
-/* new_tablelen must be power of 2 */
-static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) {
+/* new_tablelen must be power of 2 and new_tablelen == (1 <<
+ new_tablelenbits) must hold. */
+static int map_resize(ngtcp2_map *map, uint32_t new_tablelen,
+ uint32_t new_tablelenbits) {
uint32_t i;
ngtcp2_map_bucket *new_table;
ngtcp2_map_bucket *bkt;
- ngtcp2_ksl_it it;
int rv;
+ (void)rv;
new_table =
ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket));
@@ -200,64 +196,46 @@ static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) {
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
-
- if (bkt->ptr) {
- rv = map_insert(map, new_table, new_tablelen, bkt->ptr);
- if (rv != 0) {
- goto fail;
- }
- assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0);
+ if (bkt->data == NULL) {
continue;
}
+ rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
+ bkt->data);
- if (bkt->ksl) {
- for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it);
- ngtcp2_ksl_it_next(&it)) {
- rv = map_insert(map, new_table, new_tablelen, ngtcp2_ksl_it_get(&it));
- if (rv != 0) {
- goto fail;
- }
- }
- }
- }
-
- for (i = 0; i < map->tablelen; ++i) {
- bkt = &map->table[i];
- if (bkt->ksl) {
- ngtcp2_ksl_free(bkt->ksl);
- ngtcp2_mem_free(map->mem, bkt->ksl);
- }
+ assert(0 == rv);
}
ngtcp2_mem_free(map->mem, map->table);
map->tablelen = new_tablelen;
+ map->tablelenbits = new_tablelenbits;
map->table = new_table;
return 0;
-
-fail:
- for (i = 0; i < new_tablelen; ++i) {
- bkt = &new_table[i];
- if (bkt->ksl) {
- ngtcp2_ksl_free(bkt->ksl);
- ngtcp2_mem_free(map->mem, bkt->ksl);
- }
- }
-
- return rv;
}
-int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) {
+int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) {
int rv;
+ assert(data);
+
/* Load factor is 0.75 */
if ((map->size + 1) * 4 > map->tablelen * 3) {
- rv = map_resize(map, map->tablelen * 2);
- if (rv != 0) {
- return rv;
+ if (map->tablelen) {
+ rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ rv = map_resize(map, 1 << NGTCP2_INITIAL_TABLE_LENBITS,
+ NGTCP2_INITIAL_TABLE_LENBITS);
+ if (rv != 0) {
+ return rv;
+ }
}
}
- rv = map_insert(map, map->table, map->tablelen, new_entry);
+
+ rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
+ data);
if (rv != 0) {
return rv;
}
@@ -265,67 +243,93 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) {
return 0;
}
-ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key) {
- ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
- ngtcp2_ksl_it it;
+void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) {
+ uint32_t h;
+ size_t idx;
+ ngtcp2_map_bucket *bkt;
+ size_t d = 0;
- if (bkt->ptr) {
- if (bkt->ptr->key == key) {
- return bkt->ptr;
- }
+ if (map->size == 0) {
return NULL;
}
- if (bkt->ksl) {
- it = ngtcp2_ksl_lower_bound(bkt->ksl, &key);
- if (ngtcp2_ksl_it_end(&it) || *(key_type *)ngtcp2_ksl_it_key(&it) != key) {
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
return NULL;
}
- return ngtcp2_ksl_it_get(&it);
- }
- return NULL;
+ if (bkt->key == key) {
+ return bkt->data;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
}
-int ngtcp2_map_remove(ngtcp2_map *map, key_type key) {
- ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
- int rv;
+int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) {
+ uint32_t h;
+ size_t idx, didx;
+ ngtcp2_map_bucket *bkt;
+ size_t d = 0;
- if (bkt->ptr) {
- if (bkt->ptr->key == key) {
- bkt->ptr = NULL;
- --map->size;
- return 0;
- }
+ if (map->size == 0) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- if (bkt->ksl) {
- rv = ngtcp2_ksl_remove(bkt->ksl, NULL, &key);
- if (rv != 0) {
- return rv;
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
}
- --map->size;
- return 0;
- }
- return NGTCP2_ERR_INVALID_ARGUMENT;
-}
+ if (bkt->key == key) {
+ map_bucket_set_data(bkt, 0, 0, NULL);
-void ngtcp2_map_clear(ngtcp2_map *map) {
- uint32_t i;
- ngtcp2_map_bucket *bkt;
+ didx = idx;
+ idx = (idx + 1) & (map->tablelen - 1);
- for (i = 0; i < map->tablelen; ++i) {
- bkt = &map->table[i];
- bkt->ptr = NULL;
- if (bkt->ksl) {
- ngtcp2_ksl_free(bkt->ksl);
- ngtcp2_mem_free(map->mem, bkt->ksl);
- bkt->ksl = NULL;
+ for (;;) {
+ bkt = &map->table[idx];
+ if (bkt->data == NULL ||
+ distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
+ break;
+ }
+
+ map->table[didx] = *bkt;
+ map_bucket_set_data(bkt, 0, 0, NULL);
+ didx = idx;
+
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+
+ --map->size;
+
+ return 0;
}
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+void ngtcp2_map_clear(ngtcp2_map *map) {
+ if (map->tablelen == 0) {
+ return;
}
+ memset(map->table, 0, sizeof(*map->table) * map->tablelen);
map->size = 0;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h
index bbb2e705d69..a64344a9a30 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h
@@ -33,21 +33,15 @@
#include <ngtcp2/ngtcp2.h>
#include "ngtcp2_mem.h"
-#include "ngtcp2_ksl.h"
/* Implementation of unordered map */
-typedef uint64_t key_type;
-
-typedef struct ngtcp2_map_entry ngtcp2_map_entry;
-
-struct ngtcp2_map_entry {
- key_type key;
-};
+typedef uint64_t ngtcp2_map_key_type;
typedef struct ngtcp2_map_bucket {
- ngtcp2_map_entry *ptr;
- ngtcp2_ksl *ksl;
+ uint32_t hash;
+ ngtcp2_map_key_type key;
+ void *data;
} ngtcp2_map_bucket;
typedef struct ngtcp2_map {
@@ -55,18 +49,13 @@ typedef struct ngtcp2_map {
const ngtcp2_mem *mem;
size_t size;
uint32_t tablelen;
+ uint32_t tablelenbits;
} ngtcp2_map;
/*
* Initializes the map |map|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory
*/
-int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
+void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
/*
* Deallocates any resources allocated for |map|. The stored entries
@@ -78,21 +67,14 @@ void ngtcp2_map_free(ngtcp2_map *map);
/*
* Deallocates each entries using |func| function and any resources
* allocated for |map|. The |func| function is responsible for freeing
- * given the |entry| object. The |ptr| will be passed to the |func| as
+ * given the |data| object. The |ptr| will be passed to the |func| as
* send argument. The return value of the |func| will be ignored.
*/
-void ngtcp2_map_each_free(ngtcp2_map *map,
- int (*func)(ngtcp2_map_entry *entry, void *ptr),
+void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr),
void *ptr);
/*
- * Initializes the |entry| with the |key|. All entries to be inserted
- * to the map must be initialized with this function.
- */
-void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key);
-
-/*
- * Inserts the new |entry| with the key |entry->key| to the map |map|.
+ * Inserts the new |data| with the |key| to the map |map|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -102,25 +84,25 @@ void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key);
* NGTCP2_ERR_NOMEM
* Out of memory
*/
-int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry);
+int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data);
/*
- * Returns the entry associated by the key |key|. If there is no such
- * entry, this function returns NULL.
+ * Returns the data associated by the key |key|. If there is no such
+ * data, this function returns NULL.
*/
-ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key);
+void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key);
/*
- * Removes the entry associated by the key |key| from the |map|. The
- * removed entry is not freed by this function.
+ * Removes the data associated by the key |key| from the |map|. The
+ * removed data is not freed by this function.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGTCP2_ERR_INVALID_ARGUMENT
- * The entry associated by |key| does not exist.
+ * The data associated by |key| does not exist.
*/
-int ngtcp2_map_remove(ngtcp2_map *map, key_type key);
+int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key);
/*
* Removes all entries from |map|.
@@ -133,20 +115,22 @@ void ngtcp2_map_clear(ngtcp2_map *map);
size_t ngtcp2_map_size(ngtcp2_map *map);
/*
- * Applies the function |func| to each entry in the |map| with the
+ * Applies the function |func| to each data in the |map| with the
* optional user supplied pointer |ptr|.
*
* If the |func| returns 0, this function calls the |func| with the
- * next entry. If the |func| returns nonzero, it will not call the
+ * next data. If the |func| returns nonzero, it will not call the
* |func| for further entries and return the return value of the
* |func| immediately. Thus, this function returns 0 if all the
* invocations of the |func| return 0, or nonzero value which the last
* invocation of |func| returns.
*
- * Don't use this function to free each entry. Use
+ * Don't use this function to free each data. Use
* ngtcp2_map_each_free() instead.
*/
-int ngtcp2_map_each(ngtcp2_map *map,
- int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr);
+int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr),
+ void *ptr);
+
+void ngtcp2_map_print_distance(ngtcp2_map *map);
#endif /* NGTCP2_MAP_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c
index 2c036ad1634..bcce0b5cdfc 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c
@@ -25,26 +25,28 @@
*/
#include "ngtcp2_mem.h"
-static void *default_malloc(size_t size, void *mem_user_data) {
- (void)mem_user_data;
+#include <stdio.h>
+
+static void *default_malloc(size_t size, void *user_data) {
+ (void)user_data;
return malloc(size);
}
-static void default_free(void *ptr, void *mem_user_data) {
- (void)mem_user_data;
+static void default_free(void *ptr, void *user_data) {
+ (void)user_data;
free(ptr);
}
-static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
- (void)mem_user_data;
+static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
+ (void)user_data;
return calloc(nmemb, size);
}
-static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
- (void)mem_user_data;
+static void *default_realloc(void *ptr, size_t size, void *user_data) {
+ (void)user_data;
return realloc(ptr, size);
}
@@ -54,22 +56,58 @@ static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free,
const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; }
+#ifndef MEMDEBUG
void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) {
- return mem->malloc(size, mem->mem_user_data);
+ return mem->malloc(size, mem->user_data);
}
void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) {
- mem->free(ptr, mem->mem_user_data);
-}
-
-void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) {
- free_func(ptr, mem_user_data);
+ mem->free(ptr, mem->user_data);
}
void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) {
- return mem->calloc(nmemb, size, mem->mem_user_data);
+ return mem->calloc(nmemb, size, mem->user_data);
}
void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) {
- return mem->realloc(ptr, size, mem->mem_user_data);
+ return mem->realloc(ptr, size, mem->user_data);
+}
+#else /* MEMDEBUG */
+void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
+ const char *func, const char *file, size_t line) {
+ void *nptr = mem->malloc(size, mem->user_data);
+
+ fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
+ file, line);
+
+ return nptr;
+}
+
+void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ mem->free(ptr, mem->user_data);
+}
+
+void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
+ const char *func, const char *file, size_t line) {
+ void *nptr = mem->calloc(nmemb, size, mem->user_data);
+
+ fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
+ size, func, file, line);
+
+ return nptr;
+}
+
+void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->realloc(ptr, size, mem->user_data);
+
+ fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
+ size, func, file, line);
+
+ return nptr;
}
+#endif /* MEMDEBUG */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h
index cdecf8763a5..c99b6c59726 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h
@@ -34,10 +34,39 @@
/* Convenient wrapper functions to call allocator function in
|mem|. */
+#ifndef MEMDEBUG
void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size);
+
void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr);
-void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data);
+
void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size);
+
void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size);
+#else /* MEMDEBUG */
+void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define ngtcp2_mem_malloc(MEM, SIZE) \
+ ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
+
+void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line);
+
+# define ngtcp2_mem_free(MEM, PTR) \
+ ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
+
+void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define ngtcp2_mem_calloc(MEM, NMEMB, SIZE) \
+ ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \
+ __LINE__)
+
+void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define ngtcp2_mem_realloc(MEM, PTR, SIZE) \
+ ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__)
+#endif /* MEMDEBUG */
#endif /* NGTCP2_MEM_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h
new file mode 100644
index 00000000000..b1f28096174
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h
@@ -0,0 +1,136 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_NET_H
+#define NGTCP2_NET_H
+
+/* This header file is explicitly allowed to be shared with
+ ngtcp2_crypto library. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_BYTESWAP_H
+# include <byteswap.h>
+#endif /* HAVE_BYTESWAP_H */
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif /* HAVE_SYS_ENDIAN_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#if defined(HAVE_BSWAP_64) || \
+ (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
+# define ngtcp2_bswap64 bswap_64
+#else /* !HAVE_BSWAP_64 */
+# define ngtcp2_bswap64(N) \
+ ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \
+ ngtcp2_ntohl((uint32_t)((N) >> 32)))
+#endif /* !HAVE_BSWAP_64 */
+
+#if defined(HAVE_BE64TOH) || \
+ (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
+# define ngtcp2_ntohl64(N) be64toh(N)
+# define ngtcp2_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+# if defined(WORDS_BIGENDIAN)
+# define ngtcp2_ntohl64(N) (N)
+# define ngtcp2_htonl64(N) (N)
+# else /* !WORDS_BIGENDIAN */
+# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N)
+# define ngtcp2_htonl64(N) ngtcp2_bswap64(N)
+# endif /* !WORDS_BIGENDIAN */
+#endif /* !HAVE_BE64TOH */
+
+#if defined(WIN32)
+/* Windows requires ws2_32 library for ntonl family functions. We
+ define inline functions for those function so that we don't have
+ dependeny on that lib. */
+
+# ifdef _MSC_VER
+# define STIN static __inline
+# else
+# define STIN static inline
+# endif
+
+STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostlong >> 24);
+ *p++ = (hostlong >> 16) & 0xffu;
+ *p++ = (hostlong >> 8) & 0xffu;
+ *p = hostlong & 0xffu;
+ return res;
+}
+
+STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostshort >> 8);
+ *p = hostshort & 0xffu;
+ return res;
+}
+
+STIN uint32_t ngtcp2_ntohl(uint32_t netlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&netlong;
+ res = *p++ << 24;
+ res += *p++ << 16;
+ res += *p++ << 8;
+ res += *p;
+ return res;
+}
+
+STIN uint16_t ngtcp2_ntohs(uint16_t netshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&netshort;
+ res = *p++ << 8;
+ res += *p;
+ return res;
+}
+
+#else /* !WIN32 */
+
+# define ngtcp2_htonl htonl
+# define ngtcp2_htons htons
+# define ngtcp2_ntohl ntohl
+# define ngtcp2_ntohs ntohs
+
+#endif /* !WIN32 */
+
+#endif /* NGTCP2_NET_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
new file mode 100644
index 00000000000..8b06cdd5de3
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
@@ -0,0 +1,40 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_objalloc.h"
+
+void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
+ const ngtcp2_mem *mem) {
+ ngtcp2_balloc_init(&objalloc->balloc, blklen, mem);
+ ngtcp2_opl_init(&objalloc->opl);
+}
+
+void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) {
+ ngtcp2_balloc_free(&objalloc->balloc);
+}
+
+void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) {
+ ngtcp2_opl_clear(&objalloc->opl);
+ ngtcp2_balloc_clear(&objalloc->balloc);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h
new file mode 100644
index 00000000000..f1bbd3a5c94
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h
@@ -0,0 +1,140 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_OBJALLOC_H
+#define NGTCP2_OBJALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_balloc.h"
+#include "ngtcp2_opl.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+
+/*
+ * ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides
+ * an object pool with the custom allocator to reduce the allocation
+ * and deallocation overheads for small objects.
+ */
+typedef struct ngtcp2_objalloc {
+ ngtcp2_balloc balloc;
+ ngtcp2_opl opl;
+} ngtcp2_objalloc;
+
+/*
+ * ngtcp2_objalloc_init initializes |objalloc|. |blklen| is directly
+ * passed to ngtcp2_balloc_init.
+ */
+void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_objalloc_free releases all allocated resources.
+ */
+void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc);
+
+/*
+ * ngtcp2_objalloc_clear releases all allocated resources and
+ * initializes its state.
+ */
+void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc);
+
+#ifndef NOMEMPOOL
+# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void ngtcp2_objalloc_##NAME##_init( \
+ ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
+ ngtcp2_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_get( \
+ ngtcp2_objalloc *objalloc) { \
+ ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = \
+ ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE)); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \
+ ngtcp2_objalloc *objalloc, size_t len) { \
+ ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static void ngtcp2_objalloc_##NAME##_release( \
+ ngtcp2_objalloc *objalloc, TYPE *obj) { \
+ ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \
+ }
+#else /* NOMEMPOOL */
+# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void ngtcp2_objalloc_##NAME##_init( \
+ ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
+ ngtcp2_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_get( \
+ ngtcp2_objalloc *objalloc) { \
+ return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \
+ ngtcp2_objalloc *objalloc, size_t len) { \
+ return ngtcp2_mem_malloc(objalloc->balloc.mem, len); \
+ } \
+ \
+ inline static void ngtcp2_objalloc_##NAME##_release( \
+ ngtcp2_objalloc *objalloc, TYPE *obj) { \
+ ngtcp2_mem_free(objalloc->balloc.mem, obj); \
+ }
+#endif /* NOMEMPOOL */
+
+#endif /* NGTCP2_OBJALLOC_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c
new file mode 100644
index 00000000000..a29361c6349
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c
@@ -0,0 +1,46 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_opl.h"
+
+void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; }
+
+void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) {
+ ent->next = opl->head;
+ opl->head = ent;
+}
+
+ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) {
+ ngtcp2_opl_entry *ent = opl->head;
+
+ if (!ent) {
+ return NULL;
+ }
+
+ opl->head = ent->next;
+
+ return ent;
+}
+
+void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; }
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h
new file mode 100644
index 00000000000..714aa366304
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h
@@ -0,0 +1,65 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_OPL_H
+#define NGTCP2_OPL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_opl_entry ngtcp2_opl_entry;
+
+struct ngtcp2_opl_entry {
+ ngtcp2_opl_entry *next;
+};
+
+/*
+ * ngtcp2_opl is an object memory pool.
+ */
+typedef struct ngtcp2_opl {
+ ngtcp2_opl_entry *head;
+} ngtcp2_opl;
+
+/*
+ * ngtcp2_opl_init initializes |opl|.
+ */
+void ngtcp2_opl_init(ngtcp2_opl *opl);
+
+/*
+ * ngtcp2_opl_push inserts |ent| to |opl| head.
+ */
+void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent);
+
+/*
+ * ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and
+ * returns it. If |opl| does not have any entry, it returns NULL.
+ */
+ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl);
+
+void ngtcp2_opl_clear(ngtcp2_opl *opl);
+
+#endif /* NGTCP2_OPL_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c
index 3f35f28ef6a..83238730033 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c
@@ -37,6 +37,7 @@ void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) {
ngtcp2_addr_copy(&dest->local, &src->local);
ngtcp2_addr_copy(&dest->remote, &src->remote);
+ dest->user_data = src->user_data;
}
int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) {
@@ -45,30 +46,32 @@ int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) {
}
void ngtcp2_path_storage_init(ngtcp2_path_storage *ps,
- const struct sockaddr *local_addr,
- size_t local_addrlen, void *local_user_data,
- const struct sockaddr *remote_addr,
- size_t remote_addrlen, void *remote_user_data) {
- ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf,
- 0, local_user_data);
+ const ngtcp2_sockaddr *local_addr,
+ ngtcp2_socklen local_addrlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, void *user_data) {
+ ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
+ 0);
ngtcp2_addr_init(&ps->path.remote,
- (const struct sockaddr *)&ps->remote_addrbuf, 0,
- remote_user_data);
+ (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen);
ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen);
+
+ ps->path.user_data = user_data;
}
void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps,
const ngtcp2_path *path) {
ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen,
- path->local.user_data, path->remote.addr,
- path->remote.addrlen, path->remote.user_data);
+ path->remote.addr, path->remote.addrlen,
+ path->user_data);
}
void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) {
- ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf,
- 0, NULL);
+ ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
+ 0);
ngtcp2_addr_init(&ps->path.remote,
- (const struct sockaddr *)&ps->remote_addrbuf, 0, NULL);
+ (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
+ ps->path.user_data = NULL;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c
index ddfbd24bbc9..62fef7d6005 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c
@@ -26,7 +26,6 @@
#include <assert.h>
#include <string.h>
-#include <stdio.h>
#include "ngtcp2_conv.h"
#include "ngtcp2_str.h"
@@ -61,13 +60,12 @@ void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) {
ngtcp2_mem_free(mem, pc);
}
-int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
- size_t *pdcidlen, const uint8_t **pscid,
- size_t *pscidlen, const uint8_t *data,
+int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, const uint8_t *data,
size_t datalen, size_t short_dcidlen) {
size_t len;
uint32_t version;
size_t dcidlen, scidlen;
+ int supported_version;
assert(datalen);
@@ -95,23 +93,31 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
version = ngtcp2_get_uint32(&data[1]);
- if ((version == 0 || version == NGTCP2_PROTO_VER_V1 ||
- (NGTCP2_PROTO_VER_DRAFT_MIN <= version &&
- version <= NGTCP2_PROTO_VER_DRAFT_MAX)) &&
+ supported_version = ngtcp2_is_supported_version(version);
+
+ if (supported_version &&
(dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- *pversion = version;
- *pdcid = &data[6];
- *pdcidlen = dcidlen;
- *pscid = &data[6 + dcidlen + 1];
- *pscidlen = scidlen;
+ if (version && !supported_version &&
+ datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
- if (version && version != NGTCP2_PROTO_VER_V1 &&
- (version < NGTCP2_PROTO_VER_DRAFT_MIN ||
- NGTCP2_PROTO_VER_DRAFT_MAX < version)) {
- return 1;
+ dest->version = version;
+ dest->dcid = &data[6];
+ dest->dcidlen = dcidlen;
+ dest->scid = &data[6 + dcidlen + 1];
+ dest->scidlen = scidlen;
+
+ if (!version) {
+ /* VN */
+ return 0;
+ }
+
+ if (!supported_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION;
}
return 0;
}
@@ -123,11 +129,11 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- *pversion = 0;
- *pdcid = &data[1];
- *pdcidlen = short_dcidlen;
- *pscid = NULL;
- *pscidlen = 0;
+ dest->version = 0;
+ dest->dcid = &data[1];
+ dest->dcidlen = short_dcidlen;
+ dest->scid = NULL;
+ dest->scidlen = 0;
return 0;
}
@@ -170,6 +176,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
const uint8_t *token = NULL;
size_t tokenlen = 0;
uint64_t vi;
+ uint8_t flags = NGTCP2_PKT_FLAG_LONG_FORM;
if (pktlen < 5) {
return NGTCP2_ERR_INVALID_ARGUMENT;
@@ -183,16 +190,20 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
if (version == 0) {
type = NGTCP2_PKT_VERSION_NEGOTIATION;
+ /* Version Negotiation is not a long header packet. */
+ flags = NGTCP2_PKT_FLAG_NONE;
/* This must be Version Negotiation packet which lacks packet
number and payload length fields. */
len = 5 + 2;
} else {
if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) {
- return NGTCP2_ERR_INVALID_ARGUMENT;
+ flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
}
- type = ngtcp2_pkt_get_type_long(pkt[0]);
+ type = ngtcp2_pkt_get_type_long(version, pkt[0]);
switch (type) {
+ case 0:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
case NGTCP2_PKT_INITIAL:
len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN -
1; /* Cut packet number field */
@@ -267,10 +278,15 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
}
switch (type) {
- case NGTCP2_PKT_VERSION_NEGOTIATION:
case NGTCP2_PKT_RETRY:
break;
default:
+ if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
+ assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
+ /* Version Negotiation is not a long header packet. */
+ break;
+ }
+
/* Length */
n = ngtcp2_get_varint_len(p);
len += n - 1;
@@ -280,7 +296,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
}
}
- dest->flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ dest->flags = flags;
dest->type = type;
dest->version = version;
dest->pkt_num = 0;
@@ -297,11 +313,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
p += ntokenlen + tokenlen;
switch (type) {
- case NGTCP2_PKT_VERSION_NEGOTIATION:
case NGTCP2_PKT_RETRY:
dest->len = 0;
break;
default:
+ if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
+ assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
+ /* Version Negotiation is not a long header packet. */
+ dest->len = 0;
+ break;
+ }
+
vi = ngtcp2_get_varint(&n, p);
if (vi > SIZE_MAX) {
return NGTCP2_ERR_INVALID_ARGUMENT;
@@ -319,6 +341,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
size_t pktlen, size_t dcidlen) {
size_t len = 1 + dcidlen;
const uint8_t *p = pkt;
+ uint8_t flags = NGTCP2_PKT_FLAG_NONE;
assert(dcidlen <= NGTCP2_MAX_CIDLEN);
@@ -326,14 +349,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) ||
- (pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) {
+ if (pkt[0] & NGTCP2_HEADER_FORM_BIT) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
+ if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) {
+ flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
+ }
+
p = &pkt[1];
- dest->type = NGTCP2_PKT_SHORT;
+ dest->type = NGTCP2_PKT_1RTT;
ngtcp2_cid_init(&dest->dcid, p, dcidlen);
p += dcidlen;
@@ -342,11 +368,13 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
garbage. */
ngtcp2_cid_zero(&dest->scid);
- dest->flags = NGTCP2_PKT_FLAG_NONE;
+ dest->flags = flags;
dest->version = 0;
dest->len = 0;
dest->pkt_num = 0;
dest->pkt_numlen = 0;
+ dest->token.base = NULL;
+ dest->token.len = 0;
assert((size_t)(p - pkt) == len);
@@ -361,7 +389,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
len and 1 byte for packet number. */
if (hd->type != NGTCP2_PKT_RETRY) {
- len += 2 /* Length */ + hd->pkt_numlen;
+ len += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen;
}
if (hd->type == NGTCP2_PKT_INITIAL) {
@@ -374,8 +402,15 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
p = out;
- *p++ = (uint8_t)(NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK |
- (hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1));
+ *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT |
+ (ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) |
+ (uint8_t)(hd->pkt_numlen - 1));
+ if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) {
+ *p |= NGTCP2_FIXED_BIT_MASK;
+ }
+
+ ++p;
+
p = ngtcp2_put_uint32be(p, hd->version);
*p++ = (uint8_t)hd->dcid.datalen;
if (hd->dcid.datalen) {
@@ -394,7 +429,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
}
if (hd->type != NGTCP2_PKT_RETRY) {
- p = ngtcp2_put_varint14(p, (uint16_t)hd->len);
+ p = ngtcp2_put_varint30(p, (uint32_t)hd->len);
p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen);
}
@@ -414,7 +449,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
p = out;
- *p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1);
+ *p = (uint8_t)(hd->pkt_numlen - 1);
+ if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) {
+ *p |= NGTCP2_FIXED_BIT_MASK;
+ }
if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) {
*p |= NGTCP2_SHORT_KEY_PHASE_BIT;
}
@@ -1428,7 +1466,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
vi = ngtcp2_get_varint(&n, p);
if (payloadlen - len < vi) {
- return NGTCP2_FRAME_ENCODING_ERROR;
+ return NGTCP2_ERR_FRAME_ENCODING;
}
datalen = (size_t)vi;
@@ -1436,6 +1474,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
break;
default:
assert(0);
+ abort();
}
dest->type = type;
@@ -2020,8 +2059,8 @@ ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen,
ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen,
const ngtcp2_datagram *fr) {
- size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
- size_t len =
+ uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+ uint64_t len =
1 +
(fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_varint_len(datalen)) +
datalen;
@@ -2039,7 +2078,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen,
*p++ = fr->type;
if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) {
- p = ngtcp2_put_varint(p, (uint64_t)datalen);
+ p = ngtcp2_put_varint(p, datalen);
}
for (i = 0; i < fr->datacnt; ++i) {
@@ -2228,8 +2267,8 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(
/* Retry packet is sent at most once per one connection attempt. In
the first connection attempt, client has to send random DCID
- which is at least 8 bytes long. */
- if (odcid->datalen < 8) {
+ which is at least NGTCP2_MIN_INITIAL_DCIDLEN bytes long. */
+ if (odcid->datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
@@ -2244,10 +2283,16 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(
return pseudo_retrylen;
}
- if (version == NGTCP2_PROTO_VER_V1) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
- } else {
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+ break;
+ default:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
}
@@ -2330,10 +2375,16 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
pseudo_retrylen = (size_t)(p - pseudo_retry);
- if (version == NGTCP2_PROTO_VER_V1) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
- } else {
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+ break;
+ default:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
}
@@ -2353,7 +2404,7 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
}
size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
- size_t len, size_t left) {
+ uint64_t len, size_t left) {
size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) +
(offset ? ngtcp2_put_varint_len(offset) : 0);
@@ -2367,21 +2418,21 @@ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
#if SIZE_MAX > UINT32_MAX
len = ngtcp2_min(len, 4611686018427387903lu);
#endif /* SIZE_MAX > UINT32_MAX */
- return ngtcp2_min(len, left - 8);
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 8));
}
if (left > 4 + 16383 && len > 16383) {
len = ngtcp2_min(len, 1073741823);
- return ngtcp2_min(len, left - 4);
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 4));
}
if (left > 2 + 63 && len > 63) {
len = ngtcp2_min(len, 16383);
- return ngtcp2_min(len, left - 2);
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 2));
}
len = ngtcp2_min(len, 63);
- return ngtcp2_min(len, left - 1);
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 1));
}
size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) {
@@ -2417,11 +2468,101 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) {
}
size_t ngtcp2_pkt_datagram_framelen(size_t len) {
- return 1 /* type */ + ngtcp2_put_varint_len((uint64_t)len) + len;
+ return 1 /* type */ + ngtcp2_put_varint_len(len) + len;
}
-uint8_t ngtcp2_pkt_get_type_long(uint8_t c) {
- return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
+int ngtcp2_is_supported_version(uint32_t version) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ return 1;
+ default:
+ return NGTCP2_PROTO_VER_DRAFT_MIN <= version &&
+ version <= NGTCP2_PROTO_VER_DRAFT_MAX;
+ }
+}
+
+int ngtcp2_is_reserved_version(uint32_t version) {
+ return (version & NGTCP2_RESERVED_VERSION_MASK) ==
+ NGTCP2_RESERVED_VERSION_MASK;
+}
+
+uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) {
+ uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ switch (pkt_type) {
+ case NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT:
+ return NGTCP2_PKT_INITIAL;
+ case NGTCP2_PKT_TYPE_0RTT_V2_DRAFT:
+ return NGTCP2_PKT_0RTT;
+ case NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT:
+ return NGTCP2_PKT_HANDSHAKE;
+ case NGTCP2_PKT_TYPE_RETRY_V2_DRAFT:
+ return NGTCP2_PKT_RETRY;
+ default:
+ return 0;
+ }
+ default:
+ if (!ngtcp2_is_supported_version(version)) {
+ return 0;
+ }
+
+ /* QUIC v1 and draft versions share the same numeric packet
+ types. */
+ switch (pkt_type) {
+ case NGTCP2_PKT_TYPE_INITIAL_V1:
+ return NGTCP2_PKT_INITIAL;
+ case NGTCP2_PKT_TYPE_0RTT_V1:
+ return NGTCP2_PKT_0RTT;
+ case NGTCP2_PKT_TYPE_HANDSHAKE_V1:
+ return NGTCP2_PKT_HANDSHAKE;
+ case NGTCP2_PKT_TYPE_RETRY_V1:
+ return NGTCP2_PKT_RETRY;
+ default:
+ return 0;
+ }
+ }
+}
+
+uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ switch (pkt_type) {
+ case NGTCP2_PKT_INITIAL:
+ return NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT;
+ case NGTCP2_PKT_0RTT:
+ return NGTCP2_PKT_TYPE_0RTT_V2_DRAFT;
+ case NGTCP2_PKT_HANDSHAKE:
+ return NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT;
+ case NGTCP2_PKT_RETRY:
+ return NGTCP2_PKT_TYPE_RETRY_V2_DRAFT;
+ default:
+ assert(0);
+ abort();
+ }
+ default:
+ /* Assume that unsupported versions share the numeric long packet
+ types with QUIC v1 in order to send a packet to elicit Version
+ Negotiation packet. */
+
+ /* QUIC v1 and draft versions share the same numeric packet
+ types. */
+ switch (pkt_type) {
+ case NGTCP2_PKT_INITIAL:
+ return NGTCP2_PKT_TYPE_INITIAL_V1;
+ case NGTCP2_PKT_0RTT:
+ return NGTCP2_PKT_TYPE_0RTT_V1;
+ case NGTCP2_PKT_HANDSHAKE:
+ return NGTCP2_PKT_TYPE_HANDSHAKE_V1;
+ case NGTCP2_PKT_RETRY:
+ return NGTCP2_PKT_TYPE_RETRY_V1;
+ default:
+ assert(0);
+ abort();
+ }
+ }
}
int ngtcp2_pkt_verify_reserved_bits(uint8_t c) {
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h
index 708e0f6df3f..2f7838a08a5 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h
@@ -103,6 +103,40 @@
/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */
#define NGTCP2_RETRY_TAGLEN 16
+/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP payload size
+ that this library can write. */
+#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1)
+
+/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by
+ Length field in Long packet header. */
+#define NGTCP2_PKT_LENGTHLEN 4
+
+/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for
+ QUIC v1. */
+#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0
+/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC
+ v1. */
+#define NGTCP2_PKT_TYPE_0RTT_V1 0x1
+/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type
+ for QUIC v1. */
+#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2
+/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC
+ v1. */
+#define NGTCP2_PKT_TYPE_RETRY_V1 0x3
+
+/* NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT is Initial long header packet type
+ for QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT 0x1
+/* NGTCP2_PKT_TYPE_0RTT_V2_DRAFT is 0RTT long header packet type for
+ QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_0RTT_V2_DRAFT 0x2
+/* NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT is Handshake long header packet
+ type for QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT 0x3
+/* NGTCP2_PKT_TYPE_RETRY_V2_DRAFT is Retry long header packet type for
+ QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_RETRY_V2_DRAFT 0x0
+
typedef struct ngtcp2_pkt_retry {
ngtcp2_cid odcid;
ngtcp2_vec token;
@@ -258,12 +292,12 @@ typedef struct ngtcp2_stop_sending {
typedef struct ngtcp2_path_challenge {
uint8_t type;
- uint8_t data[8];
+ uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
} ngtcp2_path_challenge;
typedef struct ngtcp2_path_response {
uint8_t type;
- uint8_t data[8];
+ uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
} ngtcp2_path_response;
typedef struct ngtcp2_crypto {
@@ -293,6 +327,8 @@ typedef struct ngtcp2_handshake_done {
typedef struct ngtcp2_datagram {
uint8_t type;
+ /* dgram_id is an opaque identifier chosen by an application. */
+ uint64_t dgram_id;
/* datacnt is the number of elements that data contains. */
size_t datacnt;
/* data is a pointer to ngtcp2_vec array that stores data. */
@@ -1123,7 +1159,7 @@ int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr);
* small to write STREAM frame, this function returns (size_t)-1.
*/
size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
- size_t len, size_t left);
+ uint64_t len, size_t left);
/*
* ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes
@@ -1184,12 +1220,21 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
const ngtcp2_crypto_aead *aead,
const ngtcp2_crypto_aead_ctx *aead_ctx);
+/*
+ * ngtcp2_pkt_versioned_type returns versioned packet type for a
+ * version |version| that corresponds to the version-independent
+ * |pkt_type|.
+ */
+uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type);
+
/**
* @function
*
- * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is
- * the first byte of Long packet header.
+ * `ngtcp2_pkt_get_type_long` returns the version-independent long
+ * packet type. |version| is the QUIC version. |c| is the first byte
+ * of Long packet header. If |version| is not supported by the
+ * library, it returns 0.
*/
-uint8_t ngtcp2_pkt_get_type_long(uint8_t c);
+uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c);
#endif /* NGTCP2_PKT_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
new file mode 100644
index 00000000000..26318bb1c8e
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
@@ -0,0 +1,160 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pmtud.h"
+
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_macro.h"
+
+/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent
+ for each probe. */
+#define NGTCP2_PMTUD_PROBE_NUM_MAX 3
+
+static size_t mtu_probes[] = {
+ 1454 - 48, /* The well known MTU used by a domestic optic fiber
+ service in Japan. */
+ 1390 - 48, /* Typical Tunneled MTU */
+ 1280 - 48, /* IPv6 minimum MTU */
+ 1492 - 48, /* PPPoE */
+};
+
+static size_t mtu_probeslen = sizeof(mtu_probes) / sizeof(mtu_probes[0]);
+
+int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
+ size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
+ const ngtcp2_mem *mem) {
+ ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud));
+
+ if (pmtud == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ pmtud->mem = mem;
+ pmtud->mtu_idx = 0;
+ pmtud->num_pkts_sent = 0;
+ pmtud->expiry = UINT64_MAX;
+ pmtud->tx_pkt_num = tx_pkt_num;
+ pmtud->max_udp_payload_size = max_udp_payload_size;
+ pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size;
+ pmtud->min_fail_udp_payload_size = SIZE_MAX;
+
+ for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
+ if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
+ continue;
+ }
+ if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
+ break;
+ }
+ }
+
+ *ppmtud = pmtud;
+
+ return 0;
+}
+
+void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) {
+ if (!pmtud) {
+ return;
+ }
+
+ ngtcp2_mem_free(pmtud->mem, pmtud);
+}
+
+size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) {
+ assert(pmtud->mtu_idx < mtu_probeslen);
+
+ return mtu_probes[pmtud->mtu_idx];
+}
+
+void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
+ ngtcp2_tstamp ts) {
+ ngtcp2_tstamp timeout;
+
+ if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
+ timeout = pto;
+ } else {
+ timeout = 3 * pto;
+ }
+
+ pmtud->expiry = ts + timeout;
+}
+
+int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) {
+ return pmtud->expiry == UINT64_MAX;
+}
+
+static void pmtud_next_probe(ngtcp2_pmtud *pmtud) {
+ assert(pmtud->mtu_idx < mtu_probeslen);
+
+ ++pmtud->mtu_idx;
+ pmtud->num_pkts_sent = 0;
+ pmtud->expiry = UINT64_MAX;
+
+ for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
+ if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size ||
+ mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
+ continue;
+ }
+
+ if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) {
+ break;
+ }
+ }
+}
+
+void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) {
+ pmtud->max_udp_payload_size =
+ ngtcp2_max(pmtud->max_udp_payload_size, payloadlen);
+
+ assert(pmtud->mtu_idx < mtu_probeslen);
+
+ if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
+ return;
+ }
+
+ pmtud_next_probe(pmtud);
+}
+
+void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) {
+ if (ts < pmtud->expiry) {
+ return;
+ }
+
+ pmtud->expiry = UINT64_MAX;
+
+ if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
+ return;
+ }
+
+ pmtud->min_fail_udp_payload_size =
+ ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]);
+
+ pmtud_next_probe(pmtud);
+}
+
+int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) {
+ return pmtud->mtu_idx >= mtu_probeslen;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h
new file mode 100644
index 00000000000..6b2e691cfc7
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h
@@ -0,0 +1,123 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PMTUD_H
+#define NGTCP2_PMTUD_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_pmtud {
+ const ngtcp2_mem *mem;
+ /* mtu_idx is the index of UDP payload size candidates to try
+ out. */
+ size_t mtu_idx;
+ /* num_pkts_sent is the number of mtu_idx sized UDP datagram payload
+ sent */
+ size_t num_pkts_sent;
+ /* expiry is the expired, if it is reached, send out the next UDP
+ datagram. UINT64_MAX means no expiry, or expiration is canceled.
+ In either case, new probe packet should be sent unless we have
+ done all attempts. */
+ ngtcp2_tstamp expiry;
+ /* tx_pkt_num is the smallest outgoing packet number where the
+ current discovery is performed. In other words, acknowledging
+ packet whose packet number lower than that does not indicate the
+ success of Path MTU Discovery. */
+ int64_t tx_pkt_num;
+ /* max_udp_payload_size is the maximum UDP payload size which is
+ known to work. */
+ size_t max_udp_payload_size;
+ /* hard_max_udp_payload_size is the maximum UDP payload size that is
+ going to be probed. */
+ size_t hard_max_udp_payload_size;
+ /* min_fail_udp_payload_size is the minimum UDP payload size that is
+ known to fail. */
+ size_t min_fail_udp_payload_size;
+} ngtcp2_pmtud;
+
+/*
+ * ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its
+ * pointer to |*ppmtud|. |max_udp_payload_size| is the maximum UDP
+ * payload size that is known to work for the current path.
+ * |tx_pkt_num| should be the next packet number to send, which is
+ * used to differentiate the PMTUD probe packet sent by the previous
+ * PMTUD. PMTUD might finish immediately if |max_udp_payload_size| is
+ * larger than or equal to all UDP payload probe candidates.
+ * Therefore, call ngtcp2_pmtud_finished to check this situation.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
+ size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pmtud_del deletes |pmtud|.
+ */
+void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probelen returns the length of UDP payload size for a
+ * PMTUD probe packet.
+ */
+size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is
+ * sent.
+ */
+void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet
+ * should be sent.
+ */
+int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe
+ * UDP datagram sized |payloadlen| is acknowledged.
+ */
+void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen);
+
+/*
+ * ngtcp2_pmtud_handle_expiry handles expiry.
+ */
+void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pmtud_finished returns nonzero if PMTUD has finished.
+ */
+int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud);
+
+#endif /* NGTCP2_PMTUD_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c
index 47f4f10a29f..5376246bd4c 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c
@@ -57,7 +57,7 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) {
if (hd->type == NGTCP2_PKT_INITIAL) {
ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len;
}
- ppe->pkt_num_offset = ppe->len_offset + 2;
+ ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN;
rv = ngtcp2_pkt_encode_hd_long(
buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
} else {
@@ -115,7 +115,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) {
assert(cc->hp_mask);
if (ppe->len_offset) {
- ngtcp2_put_varint14(
+ ngtcp2_put_varint30(
buf->begin + ppe->len_offset,
(uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead));
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c
index d5f7759d6c9..314e005293c 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c
@@ -42,19 +42,12 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid,
ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log,
const ngtcp2_mem *mem) {
- int rv;
-
(*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv));
if (*ppv == NULL) {
return NGTCP2_ERR_NOMEM;
}
- rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES,
- sizeof(ngtcp2_pv_entry), mem);
- if (rv != 0) {
- ngtcp2_mem_free(mem, *ppv);
- return 0;
- }
+ ngtcp2_static_ringbuf_pv_ents_init(&(*ppv)->ents);
ngtcp2_dcid_copy(&(*ppv)->dcid, dcid);
@@ -74,7 +67,6 @@ void ngtcp2_pv_del(ngtcp2_pv *pv) {
if (pv == NULL) {
return;
}
- ngtcp2_ringbuf_free(&pv->ents);
ngtcp2_mem_free(pv->mem, pv);
}
@@ -85,11 +77,11 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
assert(pv->probe_pkt_left);
- if (ngtcp2_ringbuf_len(&pv->ents) == 0) {
+ if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
pv->started_ts = ts;
}
- ent = ngtcp2_ringbuf_push_back(&pv->ents);
+ ent = ngtcp2_ringbuf_push_back(&pv->ents.rb);
ngtcp2_pv_entry_init(ent, data, expiry, flags);
pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER;
@@ -97,7 +89,7 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
}
int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
- size_t len = ngtcp2_ringbuf_len(&pv->ents);
+ size_t len = ngtcp2_ringbuf_len(&pv->ents.rb);
size_t i;
ngtcp2_pv_entry *ent;
@@ -106,7 +98,7 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
}
for (i = 0; i < len; ++i) {
- ent = ngtcp2_ringbuf_get(&pv->ents, i);
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, i);
if (memcmp(ent->data, data, sizeof(ent->data)) == 0) {
*pflags = ent->flags;
ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated");
@@ -120,11 +112,11 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
ngtcp2_pv_entry *ent;
- if (ngtcp2_ringbuf_len(&pv->ents) == 0) {
+ if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
return;
}
- ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
if (ent->expiry > ts) {
return;
@@ -146,9 +138,9 @@ int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
return 0;
}
- assert(ngtcp2_ringbuf_len(&pv->ents));
+ assert(ngtcp2_ringbuf_len(&pv->ents.rb));
- ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
t = pv->started_ts + pv->timeout;
t = ngtcp2_max(t, ent->expiry);
@@ -160,11 +152,11 @@ ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) {
ngtcp2_pv_entry *ent;
if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) ||
- ngtcp2_ringbuf_len(&pv->ents) == 0) {
+ ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
return UINT64_MAX;
}
- ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
return ent->expiry;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h
index b532dbca983..293cbcaaf6e 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h
@@ -46,10 +46,10 @@ typedef struct ngtcp2_log ngtcp2_log;
typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
/* NGTCP2_PV_ENTRY_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00
+#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00u
/* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which
contains PATH_CHALLENGE is undersized (< 1200 bytes) */
-#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01
+#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01u
typedef struct ngtcp2_pv_entry {
/* expiry is the timestamp when this PATH_CHALLENGE expires. */
@@ -64,25 +64,30 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
ngtcp2_tstamp expiry, uint8_t flags);
/* NGTCP2_PV_FLAG_NONE indicates no flag is set. */
-#define NGTCP2_PV_FLAG_NONE 0x00
+#define NGTCP2_PV_FLAG_NONE 0x00u
/* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path
validation should be ignored entirely. */
-#define NGTCP2_PV_FLAG_DONT_CARE 0x01
+#define NGTCP2_PV_FLAG_DONT_CARE 0x01u
/* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is
cancelled. */
-#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02
+#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02u
/* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID is
available in ngtcp2_pv. If path validation fails, fallback to the
fallback DCID. If path validation succeeds, fallback DCID is
retired if it does not equal to the current DCID. */
-#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04
+#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04u
/* NGTCP2_PV_FLAG_MTU_PROBE indicates that a validation must probe
least MTU that QUIC requires, which is 1200 bytes. If it fails, a
path is not viable. */
-#define NGTCP2_PV_FLAG_MTU_PROBE 0x08
+#define NGTCP2_PV_FLAG_MTU_PROBE 0x08u
+/* NGTCP2_PV_FLAG_PREFERRED_ADDR indicates that client is migrating to
+ server's preferred address. This flag is only used by client. */
+#define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10u
typedef struct ngtcp2_pv ngtcp2_pv;
+ngtcp2_static_ringbuf_def(pv_ents, NGTCP2_PV_MAX_ENTRIES,
+ sizeof(ngtcp2_pv_entry));
/*
* ngtcp2_pv is the context of a single path validation.
*/
@@ -95,7 +100,7 @@ struct ngtcp2_pv {
fallback if this path validation fails. */
ngtcp2_dcid fallback_dcid;
/* ents is the ring buffer of ngtcp2_pv_entry */
- ngtcp2_ringbuf ents;
+ ngtcp2_static_ringbuf_pv_ents ents;
/* timeout is the duration within which this path validation should
succeed. */
ngtcp2_duration timeout;
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c
index 7c31ab64af6..69eaeb73674 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c
@@ -28,6 +28,8 @@
#include "ngtcp2_str.h"
#include "ngtcp2_vec.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_net.h"
void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write,
ngtcp2_tstamp ts, void *user_data) {
@@ -185,8 +187,8 @@ static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name,
static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) {
p = write_verbatim(
- p, "\"common_fields\":{\"protocol_type\":\"QUIC_HTTP3\",\"time_format\":"
- "\"relative\",\"reference_time\":\"0\",\"group_id\":");
+ p, "\"common_fields\":{\"protocol_type\":[\"QUIC\"],\"time_format\":"
+ "\"relative\",\"reference_time\":0,\"group_id\":");
p = write_cid(p, odcid);
*p++ = '}';
return p;
@@ -215,7 +217,7 @@ void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) {
}
p = write_verbatim(
- p, "{\"qlog_format\":\"NDJSON\",\"qlog_version\":\"draft-02\",");
+ p, "\x1e{\"qlog_format\":\"JSON-SEQ\",\"qlog_version\":\"0.3\",");
p = write_trace(p, server, odcid);
p = write_verbatim(p, "}\n");
@@ -238,6 +240,10 @@ static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake");
static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT");
static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT");
static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry");
+static ngtcp2_vec vec_pkt_type_version_negotiation =
+ ngtcp2_make_vec_lit("version_negotiation");
+static ngtcp2_vec vec_pkt_type_stateless_reset =
+ ngtcp2_make_vec_lit("stateless_reset");
static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown");
static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) {
@@ -256,19 +262,33 @@ static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) {
}
}
- return &vec_pkt_type_1rtt;
+ switch (hd->type) {
+ case NGTCP2_PKT_VERSION_NEGOTIATION:
+ return &vec_pkt_type_version_negotiation;
+ case NGTCP2_PKT_STATELESS_RESET:
+ return &vec_pkt_type_stateless_reset;
+ case NGTCP2_PKT_1RTT:
+ return &vec_pkt_type_1rtt;
+ default:
+ return &vec_pkt_type_unknown;
+ }
}
static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) {
/*
- * {"packet_type":"version_negotiation","packet_number":"0000000000000000000"}
+ * {"packet_type":"version_negotiation","packet_number":"0000000000000000000","token":{"data":""}}
*/
-#define NGTCP2_QLOG_PKT_HD_OVERHEAD 75
+#define NGTCP2_QLOG_PKT_HD_OVERHEAD 95
*p++ = '{';
p = write_pair(p, "packet_type", qlog_pkt_type(hd));
*p++ = ',';
p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num);
+ if (hd->type == NGTCP2_PKT_INITIAL && hd->token.len) {
+ p = write_verbatim(p, ",\"token\":{");
+ p = write_pair_hex(p, "data", hd->token.base, hd->token.len);
+ *p++ = '}';
+ }
/* TODO Write DCIL and DCID */
/* TODO Write SCIL and SCID */
*p++ = '}';
@@ -313,10 +333,7 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
p = write_verbatim(p, "{\"frame_type\":\"ack\",");
p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled);
- *p++ = ',';
- p = write_string(p, "acked_ranges");
- *p++ = ':';
- *p++ = '[';
+ p = write_verbatim(p, ",\"acked_ranges\":[");
largest_ack = fr->largest_ack;
min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen;
@@ -410,14 +427,15 @@ static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) {
static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) {
/*
- * {"frame_type":"new_token","length":0000000000000000000,"token":""}
+ * {"frame_type":"new_token","length":0000000000000000000,"token":{"data":""}}
*/
-#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66
+#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 75
p = write_verbatim(p, "{\"frame_type\":\"new_token\",");
p = write_pair_number(p, "length", fr->token.len);
- *p++ = ',';
- p = write_pair_hex(p, "token", fr->token.base, fr->token.len);
+ p = write_verbatim(p, ",\"token\":{");
+ p = write_pair_hex(p, "data", fr->token.base, fr->token.len);
+ *p++ = '}';
*p++ = '}';
return p;
@@ -480,9 +498,7 @@ static uint8_t *write_max_streams_frame(uint8_t *p,
*/
#define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89
- p = write_verbatim(p, "{\"frame_type\":\"max_streams\",");
- p = write_string(p, "stream_type");
- *p++ = ':';
+ p = write_verbatim(p, "{\"frame_type\":\"max_streams\",\"stream_type\":");
if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) {
p = write_string(p, "bidirectional");
} else {
@@ -541,9 +557,9 @@ static uint8_t *write_streams_blocked_frame(uint8_t *p,
static uint8_t *
write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) {
/*
- * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
+ * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":{"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}}
*/
-#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 271
+#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 280
p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\",");
p = write_pair_number(p, "sequence_number", fr->seq);
@@ -553,10 +569,11 @@ write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) {
p = write_pair_number(p, "connection_id_length", fr->cid.datalen);
*p++ = ',';
p = write_pair_cid(p, "connection_id", &fr->cid);
- *p++ = ',';
- p = write_pair_hex(p, "stateless_reset_token", fr->stateless_reset_token,
+ p = write_verbatim(p, ",\"stateless_reset_token\":{");
+ p = write_pair_hex(p, "data", fr->stateless_reset_token,
sizeof(fr->stateless_reset_token));
*p++ = '}';
+ *p++ = '}';
return p;
}
@@ -611,9 +628,8 @@ write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) {
*/
#define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131
- p = write_verbatim(p, "{\"frame_type\":\"connection_close\",");
- p = write_string(p, "error_space");
- *p++ = ':';
+ p = write_verbatim(p,
+ "{\"frame_type\":\"connection_close\",\"error_space\":");
if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
p = write_string(p, "transport");
} else {
@@ -669,6 +685,7 @@ static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) {
ngtcp2_buf_reset(&qlog->buf);
p = qlog->buf.last;
+ *p++ = '\x1e';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(p, ",\"name\":");
@@ -690,14 +707,18 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
}
/*
- * ],"header":,"raw":{"packet_size":0000000000000000000}}}
+ * ],"header":,"raw":{"length":0000000000000000000}}}
*
* plus, terminating LF
*/
#define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD \
- (1 + 55 + NGTCP2_QLOG_PKT_HD_OVERHEAD)
+ (1 + 50 + NGTCP2_QLOG_PKT_HD_OVERHEAD)
+
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD + hd->token.len * 2) {
+ return;
+ }
- assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD);
assert(ngtcp2_buf_len(&qlog->buf));
/* Eat last ',' */
@@ -707,7 +728,7 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
p = write_verbatim(p, "],\"header\":");
p = write_pkt_hd(p, hd);
- p = write_verbatim(p, ",\"raw\":{\"packet_size\":");
+ p = write_verbatim(p, ",\"raw\":{\"length\":");
p = write_number(p, pktlen);
p = write_verbatim(p, "}}}\n");
@@ -726,15 +747,13 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
switch (fr->type) {
case NGTCP2_FRAME_PADDING:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) {
return;
}
p = write_padding_frame(p, &fr->padding);
break;
case NGTCP2_FRAME_PING:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1) {
return;
}
p = write_ping_frame(p, &fr->ping);
@@ -746,86 +765,75 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
(size_t)(fr->type == NGTCP2_FRAME_ACK_ECN
? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD
: 0) +
- NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1) {
return;
}
p = write_ack_frame(p, &fr->ack);
break;
case NGTCP2_FRAME_RESET_STREAM:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD +
- 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + 1) {
return;
}
p = write_reset_stream_frame(p, &fr->reset_stream);
break;
case NGTCP2_FRAME_STOP_SENDING:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD +
- 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + 1) {
return;
}
p = write_stop_sending_frame(p, &fr->stop_sending);
break;
case NGTCP2_FRAME_CRYPTO:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1) {
return;
}
p = write_crypto_frame(p, &fr->crypto);
break;
case NGTCP2_FRAME_NEW_TOKEN:
if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD +
- fr->new_token.token.len * 2 + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ fr->new_token.token.len * 2 + 1) {
return;
}
p = write_new_token_frame(p, &fr->new_token);
break;
case NGTCP2_FRAME_STREAM:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1) {
return;
}
p = write_stream_frame(p, &fr->stream);
break;
case NGTCP2_FRAME_MAX_DATA:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1) {
return;
}
p = write_max_data_frame(p, &fr->max_data);
break;
case NGTCP2_FRAME_MAX_STREAM_DATA:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1) {
return;
}
p = write_max_stream_data_frame(p, &fr->max_stream_data);
break;
case NGTCP2_FRAME_MAX_STREAMS_BIDI:
case NGTCP2_FRAME_MAX_STREAMS_UNI:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD +
- 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + 1) {
return;
}
p = write_max_streams_frame(p, &fr->max_streams);
break;
case NGTCP2_FRAME_DATA_BLOCKED:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD +
- 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + 1) {
return;
}
p = write_data_blocked_frame(p, &fr->data_blocked);
break;
case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1) {
return;
}
p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked);
@@ -833,40 +841,35 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1) {
return;
}
p = write_streams_blocked_frame(p, &fr->streams_blocked);
break;
case NGTCP2_FRAME_NEW_CONNECTION_ID:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1) {
return;
}
p = write_new_connection_id_frame(p, &fr->new_connection_id);
break;
case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1) {
return;
}
p = write_retire_connection_id_frame(p, &fr->retire_connection_id);
break;
case NGTCP2_FRAME_PATH_CHALLENGE:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1) {
return;
}
p = write_path_challenge_frame(p, &fr->path_challenge);
break;
case NGTCP2_FRAME_PATH_RESPONSE:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD +
- 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + 1) {
return;
}
p = write_path_response_frame(p, &fr->path_response);
@@ -874,24 +877,21 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
case NGTCP2_FRAME_CONNECTION_CLOSE:
case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1) {
return;
}
p = write_connection_close_frame(p, &fr->connection_close);
break;
case NGTCP2_FRAME_HANDSHAKE_DONE:
if (ngtcp2_buf_left(&qlog->buf) <
- NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1) {
return;
}
p = write_handshake_done_frame(p, &fr->handshake_done);
break;
case NGTCP2_FRAME_DATAGRAM:
case NGTCP2_FRAME_DATAGRAM_LEN:
- if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1 +
- NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1) {
return;
}
p = write_datagram_frame(p, &fr->datagram);
@@ -934,6 +934,7 @@ void ngtcp2_qlog_parameters_set_transport_params(
return;
}
+ *p++ = '\x1e';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(
@@ -958,9 +959,10 @@ void ngtcp2_qlog_parameters_set_transport_params(
*p++ = ',';
}
if (params->stateless_reset_token_present) {
- p = write_pair_hex(p, "stateless_reset_token",
- params->stateless_reset_token,
+ p = write_verbatim(p, "\"stateless_reset_token\":{");
+ p = write_pair_hex(p, "data", params->stateless_reset_token,
sizeof(params->stateless_reset_token));
+ *p++ = '}';
*p++ = ',';
}
p = write_pair_bool(p, "disable_active_migration",
@@ -1009,14 +1011,17 @@ void ngtcp2_qlog_parameters_set_transport_params(
p = write_pair_number(p, "port_v6", paddr->ipv6_port);
*p++ = ',';
p = write_pair_cid(p, "connection_id", &paddr->cid);
- *p++ = ',';
- p = write_pair_hex(p, "stateless_reset_token", paddr->stateless_reset_token,
+ p = write_verbatim(p, ",\"stateless_reset_token\":{");
+ p = write_pair_hex(p, "data", paddr->stateless_reset_token,
sizeof(paddr->stateless_reset_token));
*p++ = '}';
+ *p++ = '}';
}
*p++ = ',';
p = write_pair_number(p, "max_datagram_frame_size",
params->max_datagram_frame_size);
+ *p++ = ',';
+ p = write_pair_bool(p, "grease_quic_bit", params->grease_quic_bit);
p = write_verbatim(p, "}}\n");
qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
@@ -1032,6 +1037,7 @@ void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog,
return;
}
+ *p++ = '\x1e';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{");
@@ -1071,6 +1077,7 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) {
return;
}
+ *p++ = '\x1e';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(
@@ -1087,22 +1094,110 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) {
(size_t)(p - buf));
}
-void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog,
- const ngtcp2_pkt_hd *hd) {
+void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_pkt_retry *retry) {
+ uint8_t rawbuf[1024];
+ ngtcp2_buf buf;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
+
+ *buf.last++ = '\x1e';
+ *buf.last++ = '{';
+ buf.last = qlog_write_time(qlog, buf.last);
+ buf.last = write_verbatim(
+ buf.last,
+ ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
+
+ if (ngtcp2_buf_left(&buf) <
+ NGTCP2_QLOG_PKT_HD_OVERHEAD + hd->token.len * 2 +
+ sizeof(",\"retry_token\":{\"data\":\"\"}}}\n") - 1 +
+ retry->token.len * 2) {
+ return;
+ }
+
+ buf.last = write_pkt_hd(buf.last, hd);
+ buf.last = write_verbatim(buf.last, ",\"retry_token\":{");
+ buf.last =
+ write_pair_hex(buf.last, "data", retry->token.base, retry->token.len);
+ buf.last = write_verbatim(buf.last, "}}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos,
+ ngtcp2_buf_len(&buf));
+}
+
+void ngtcp2_qlog_stateless_reset_pkt_received(
+ ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) {
uint8_t buf[256];
uint8_t *p = buf;
+ ngtcp2_pkt_hd hd = {0};
if (!qlog->write) {
return;
}
+ hd.type = NGTCP2_PKT_STATELESS_RESET;
+
+ *p++ = '\x1e';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(
p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
- p = write_pkt_hd(p, hd);
+ p = write_pkt_hd(p, &hd);
+ *p++ = ',';
+ p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token,
+ NGTCP2_STATELESS_RESET_TOKENLEN);
p = write_verbatim(p, "}}\n");
qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
(size_t)(p - buf));
}
+
+void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv,
+ size_t nsv) {
+ uint8_t rawbuf[512];
+ ngtcp2_buf buf;
+ size_t i;
+ uint32_t v;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
+
+ *buf.last++ = '\x1e';
+ *buf.last++ = '{';
+ buf.last = qlog_write_time(qlog, buf.last);
+ buf.last = write_verbatim(
+ buf.last,
+ ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
+ buf.last = write_pkt_hd(buf.last, hd);
+ buf.last = write_verbatim(buf.last, ",\"supported_versions\":[");
+
+ if (nsv) {
+ if (ngtcp2_buf_left(&buf) <
+ (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) {
+ return;
+ }
+
+ v = ngtcp2_htonl(sv[0]);
+ buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v));
+
+ for (i = 1; i < nsv; ++i) {
+ *buf.last++ = ',';
+ v = ngtcp2_htonl(sv[i]);
+ buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v));
+ }
+ }
+
+ buf.last = write_verbatim(buf.last, "]}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos,
+ ngtcp2_buf_len(&buf));
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h
index cb3c2063f76..b9107c0e5c0 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h
@@ -139,6 +139,23 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent);
* ngtcp2_qlog_retry_pkt_received writes packet_received event for a
* received Retry packet.
*/
-void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd);
+void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_pkt_retry *retry);
+
+/*
+ * ngtcp2_qlog_stateless_reset_pkt_received writes packet_received
+ * event for a received Stateless Reset packet.
+ */
+void ngtcp2_qlog_stateless_reset_pkt_received(
+ ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr);
+
+/*
+ * ngtcp2_qlog_version_negotiation_pkt_received writes packet_received
+ * event for a received Version Negotiation packet.
+ */
+void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv,
+ size_t nsv);
#endif /* NGTCP2_QLOG_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h
index e392c34ebfe..4cb40882192 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h
@@ -31,12 +31,10 @@
#include <ngtcp2/ngtcp2.h>
-/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in
- draft-ietf-quic-recovery-22. */
+/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in RFC 9002. */
#define NGTCP2_PKT_THRESHOLD 3
-/* NGTCP2_GRANULARITY is kGranularity described in
- draft-ietf-quic-recovery-17. */
+/* NGTCP2_GRANULARITY is kGranularity described in RFC 9002. */
#define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS
#endif /* NGTCP2_RCVRY_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
index e4deab1ff76..a6b3f73e733 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
@@ -43,24 +43,30 @@ unsigned int __popcnt(unsigned int x) {
int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
const ngtcp2_mem *mem) {
+ uint8_t *buf = ngtcp2_mem_malloc(mem, nmemb * size);
+ if (buf == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_ringbuf_buf_init(rb, nmemb, size, buf, mem);
+
+ return 0;
+}
+
+void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ uint8_t *buf, const ngtcp2_mem *mem) {
#ifdef WIN32
assert(1 == __popcnt((unsigned int)nmemb));
#else
assert(1 == __builtin_popcount((unsigned int)nmemb));
#endif
- rb->buf = ngtcp2_mem_malloc(mem, nmemb * size);
- if (rb->buf == NULL) {
- return NGTCP2_ERR_NOMEM;
- }
-
+ rb->buf = buf;
rb->mem = mem;
rb->nmemb = nmemb;
rb->size = size;
rb->first = 0;
rb->len = 0;
-
- return 0;
}
void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) {
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
index c6e14215183..16635c94103 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
@@ -63,6 +63,13 @@ int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
const ngtcp2_mem *mem);
/*
+ * ngtcp2_ringbuf_buf_init initializes |rb| with given buffer and
+ * size.
+ */
+void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ uint8_t *buf, const ngtcp2_mem *mem);
+
+/*
* ngtcp2_ringbuf_free frees resources allocated for |rb|. This
* function does not free the memory pointed by |rb|.
*/
@@ -107,4 +114,19 @@ void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset);
/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */
int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb);
+/* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper
+ which uses a statically allocated buffer that is suitable for a
+ usage that does not change buffer size with ngtcp2_ringbuf_resize.
+ ngtcp2_ringbuf_free should never be called for rb field. */
+#define ngtcp2_static_ringbuf_def(NAME, NMEMB, SIZE) \
+ typedef struct ngtcp2_static_ringbuf_##NAME { \
+ ngtcp2_ringbuf rb; \
+ uint8_t buf[(NMEMB) * (SIZE)]; \
+ } ngtcp2_static_ringbuf_##NAME; \
+ \
+ static inline void ngtcp2_static_ringbuf_##NAME##_init( \
+ ngtcp2_static_ringbuf_##NAME *srb) { \
+ ngtcp2_ringbuf_buf_init(&srb->rb, (NMEMB), (SIZE), srb->buf, NULL); \
+ }
+
#endif /* NGTCP2_RINGBUF_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c
index 499c07ec6be..9c3d75dc33a 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c
@@ -69,11 +69,8 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
int rv;
ngtcp2_rob_gap *g;
- rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar,
- sizeof(ngtcp2_range), mem);
- if (rv != 0) {
- goto fail_gapksl_ksl_init;
- }
+ ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+ mem);
rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem);
if (rv != 0) {
@@ -85,23 +82,18 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
goto fail_gapksl_ksl_insert;
}
- rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar,
- sizeof(ngtcp2_range), mem);
- if (rv != 0) {
- goto fail_dataksl_ksl_init;
- }
+ ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+ mem);
rob->chunk = chunk;
rob->mem = mem;
return 0;
-fail_dataksl_ksl_init:
fail_gapksl_ksl_insert:
ngtcp2_rob_gap_del(g, mem);
fail_rob_gap_new:
ngtcp2_ksl_free(&rob->gapksl);
-fail_gapksl_ksl_init:
return rv;
}
@@ -185,7 +177,7 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
break;
}
if (ngtcp2_range_eq(&g->range, &m)) {
- ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range);
+ ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range);
ngtcp2_rob_gap_del(g, rob->mem);
rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
(size_t)ngtcp2_range_len(&m));
@@ -244,7 +236,7 @@ int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) {
g->range.begin = offset;
break;
}
- ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range);
+ ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range);
ngtcp2_rob_gap_del(g, rob->mem);
}
@@ -255,7 +247,7 @@ int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) {
if (offset < d->range.begin + rob->chunk) {
return 0;
}
- ngtcp2_ksl_remove(&rob->dataksl, &it, &d->range);
+ ngtcp2_ksl_remove_hint(&rob->dataksl, &it, &it, &d->range);
ngtcp2_rob_data_del(d, rob->mem);
}
@@ -305,7 +297,7 @@ void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) {
return;
}
- ngtcp2_ksl_remove(&rob->dataksl, NULL, &d->range);
+ ngtcp2_ksl_remove_hint(&rob->dataksl, NULL, &it, &d->range);
ngtcp2_rob_data_del(d, rob->mem);
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c
index e546fdf85c6..7b50f98d41e 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c
@@ -23,6 +23,9 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ngtcp2_rst.h"
+
+#include <assert.h>
+
#include "ngtcp2_rtb.h"
#include "ngtcp2_cc.h"
#include "ngtcp2_macro.h"
@@ -32,6 +35,9 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) {
rs->delivered = 0;
rs->prior_delivered = 0;
rs->prior_ts = 0;
+ rs->tx_in_flight = 0;
+ rs->lost = 0;
+ rs->prior_lost = 0;
rs->send_elapsed = 0;
rs->ack_elapsed = 0;
rs->is_app_limited = 0;
@@ -39,10 +45,15 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) {
void ngtcp2_rst_init(ngtcp2_rst *rst) {
ngtcp2_rs_init(&rst->rs);
+ ngtcp2_window_filter_init(&rst->wf, 12);
rst->delivered = 0;
rst->delivered_ts = 0;
rst->first_sent_ts = 0;
rst->app_limited = 0;
+ rst->next_round_delivered = 0;
+ rst->round_count = 0;
+ rst->is_cwnd_limited = 0;
+ rst->lost = 0;
}
void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
@@ -54,15 +65,24 @@ void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
ent->rst.delivered_ts = rst->delivered_ts;
ent->rst.delivered = rst->delivered;
ent->rst.is_app_limited = rst->app_limited != 0;
+ ent->rst.tx_in_flight = cstat->bytes_in_flight + ent->pktlen;
+ ent->rst.lost = rst->lost;
}
-int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
+int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat,
+ uint64_t pkt_delivered) {
ngtcp2_rs *rs = &rst->rs;
+ uint64_t rate;
if (rst->app_limited && rst->delivered > rst->app_limited) {
rst->app_limited = 0;
}
+ if (pkt_delivered >= rst->next_round_delivered) {
+ rst->next_round_delivered = pkt_delivered;
+ ++rst->round_count;
+ }
+
if (rs->prior_ts == 0) {
return 0;
}
@@ -70,14 +90,22 @@ int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed);
rs->delivered = rst->delivered - rs->prior_delivered;
+ rs->lost = rst->lost - rs->prior_lost;
if (rs->interval < cstat->min_rtt) {
rs->interval = UINT64_MAX;
return 0;
}
- if (rs->interval) {
- cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval;
+ if (!rs->interval) {
+ return 0;
+ }
+
+ rate = rs->delivered * NGTCP2_SECONDS / rs->interval;
+
+ if (rate > ngtcp2_window_filter_get_best(&rst->wf) || !rst->app_limited) {
+ ngtcp2_window_filter_update(&rst->wf, rate, rst->round_count);
+ cstat->delivery_rate_sec = ngtcp2_window_filter_get_best(&rst->wf);
}
return 0;
@@ -96,6 +124,8 @@ void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
rs->is_app_limited = ent->rst.is_app_limited;
rs->send_elapsed = ent->ts - ent->rst.first_sent_ts;
rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts;
+ rs->tx_in_flight = ent->rst.tx_in_flight;
+ rs->prior_lost = ent->rst.lost;
rst->first_sent_ts = ent->ts;
}
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h
index 14aae998ebd..488c65575a5 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h
@@ -31,6 +31,8 @@
#include <ngtcp2/ngtcp2.h>
+#include "ngtcp2_window_filter.h"
+
typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
/**
@@ -43,6 +45,9 @@ typedef struct ngtcp2_rs {
uint64_t delivered;
uint64_t prior_delivered;
ngtcp2_tstamp prior_ts;
+ uint64_t tx_in_flight;
+ uint64_t lost;
+ uint64_t prior_lost;
ngtcp2_duration send_elapsed;
ngtcp2_duration ack_elapsed;
int is_app_limited;
@@ -56,17 +61,23 @@ void ngtcp2_rs_init(ngtcp2_rs *rs);
*/
typedef struct ngtcp2_rst {
ngtcp2_rs rs;
+ ngtcp2_window_filter wf;
uint64_t delivered;
ngtcp2_tstamp delivered_ts;
ngtcp2_tstamp first_sent_ts;
uint64_t app_limited;
+ uint64_t next_round_delivered;
+ uint64_t round_count;
+ uint64_t lost;
+ int is_cwnd_limited;
} ngtcp2_rst;
void ngtcp2_rst_init(ngtcp2_rst *rst);
void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
const ngtcp2_conn_stat *cstat);
-int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat);
+int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat,
+ uint64_t pkt_delivered);
void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
ngtcp2_tstamp ts);
void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat);
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c
index 1ef67ccbc6a..644071400a6 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c
@@ -46,6 +46,18 @@ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) {
return 0;
}
+int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
+ ngtcp2_objalloc *objalloc) {
+ *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc);
+ if (*pfrc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_frame_chain_init(*pfrc);
+
+ return 0;
+}
+
int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
const ngtcp2_mem *mem) {
*pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen);
@@ -58,35 +70,44 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
return 0;
}
-int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc,
- size_t datacnt,
- const ngtcp2_mem *mem) {
- size_t need = sizeof(ngtcp2_vec) * (datacnt - 1);
- size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream);
+int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream);
- if (datacnt > 0 && need > avail) {
- return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ if (datacnt > 1) {
+ need = sizeof(ngtcp2_vec) * (datacnt - 1);
+
+ if (need > avail) {
+ return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ }
}
- return ngtcp2_frame_chain_new(pfrc, mem);
+ return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
}
-int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc,
- size_t datacnt,
- const ngtcp2_mem *mem) {
- size_t need = sizeof(ngtcp2_vec) * (datacnt - 1);
- size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto);
+int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto);
+
+ if (datacnt > 1) {
+ need = sizeof(ngtcp2_vec) * (datacnt - 1);
- if (datacnt > 0 && need > avail) {
- return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ if (need > avail) {
+ return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ }
}
- return ngtcp2_frame_chain_new(pfrc, mem);
+ return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
}
-int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
- const ngtcp2_vec *token,
- const ngtcp2_mem *mem) {
+int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
+ const ngtcp2_vec *token,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token);
int rv;
uint8_t *p;
@@ -95,7 +116,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
if (token->len > avail) {
rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem);
} else {
- rv = ngtcp2_frame_chain_new(pfrc, mem);
+ rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
}
if (rv != 0) {
return rv;
@@ -104,7 +125,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
fr = &(*pfrc)->fr;
fr->type = NGTCP2_FRAME_NEW_TOKEN;
- p = (uint8_t *)(*pfrc) + sizeof(ngtcp2_new_token);
+ p = (uint8_t *)fr + sizeof(ngtcp2_new_token);
memcpy(p, token->base, token->len);
ngtcp2_vec_init(&fr->new_token.token, p, token->len);
@@ -127,19 +148,71 @@ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) {
ngtcp2_mem_free(mem, frc);
}
+void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_binder *binder;
+
+ if (frc == NULL) {
+ return;
+ }
+
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ if (frc->fr.stream.datacnt &&
+ sizeof(ngtcp2_vec) * (frc->fr.stream.datacnt - 1) >
+ sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream)) {
+ ngtcp2_frame_chain_del(frc, mem);
+
+ return;
+ }
+
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ if (frc->fr.crypto.datacnt &&
+ sizeof(ngtcp2_vec) * (frc->fr.crypto.datacnt - 1) >
+ sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto)) {
+ ngtcp2_frame_chain_del(frc, mem);
+
+ return;
+ }
+
+ break;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ if (frc->fr.new_token.token.len >
+ sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) {
+ ngtcp2_frame_chain_del(frc, mem);
+
+ return;
+ }
+
+ break;
+ }
+
+ binder = frc->binder;
+ if (binder && --binder->refcount == 0) {
+ ngtcp2_mem_free(mem, binder);
+ }
+
+ frc->binder = NULL;
+
+ ngtcp2_objalloc_frame_chain_release(objalloc, frc);
+}
+
void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) {
frc->next = NULL;
frc->binder = NULL;
}
-void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc,
- const ngtcp2_mem *mem) {
+void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
ngtcp2_frame_chain *next;
- for (; frc;) {
+ for (; frc; frc = next) {
next = frc->next;
- ngtcp2_frame_chain_del(frc, mem);
- frc = next;
+
+ ngtcp2_frame_chain_objalloc_del(frc, objalloc, mem);
}
}
@@ -176,35 +249,46 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
return 0;
}
-int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd,
- ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
- size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) {
- (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry));
+static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+ size_t pktlen, uint16_t flags) {
+ memset(ent, 0, sizeof(*ent));
+
+ ent->hd.pkt_num = hd->pkt_num;
+ ent->hd.type = hd->type;
+ ent->hd.flags = hd->flags;
+ ent->frc = frc;
+ ent->ts = ts;
+ ent->lost_ts = UINT64_MAX;
+ ent->pktlen = pktlen;
+ ent->flags = flags;
+ ent->next = NULL;
+}
+
+int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
+ const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+ size_t pktlen, uint16_t flags,
+ ngtcp2_objalloc *objalloc) {
+ *pent = ngtcp2_objalloc_rtb_entry_get(objalloc);
if (*pent == NULL) {
return NGTCP2_ERR_NOMEM;
}
- (*pent)->hd.pkt_num = hd->pkt_num;
- (*pent)->hd.type = hd->type;
- (*pent)->hd.flags = hd->flags;
- (*pent)->frc = frc;
- (*pent)->ts = ts;
- (*pent)->lost_ts = UINT64_MAX;
- (*pent)->pktlen = pktlen;
- (*pent)->flags = flags;
- (*pent)->next = NULL;
+ rtb_entry_init(*pent, hd, frc, ts, pktlen, flags);
return 0;
}
-void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) {
- if (ent == NULL) {
- return;
- }
+void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent,
+ ngtcp2_objalloc *objalloc,
+ ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_list_objalloc_del(ent->frc, frc_objalloc, mem);
- ngtcp2_frame_chain_list_del(ent->frc, mem);
+ ent->frc = NULL;
- ngtcp2_mem_free(mem, ent);
+ ngtcp2_objalloc_rtb_entry_release(objalloc, ent);
}
static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
@@ -214,7 +298,10 @@ static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc,
ngtcp2_log *log, ngtcp2_qlog *qlog,
- const ngtcp2_mem *mem) {
+ ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
+ rtb->rtb_entry_objalloc = rtb_entry_objalloc;
+ rtb->frc_objalloc = frc_objalloc;
ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem);
rtb->crypto = crypto;
rtb->rst = rst;
@@ -225,12 +312,14 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
rtb->largest_acked_tx_pkt_num = -1;
rtb->num_ack_eliciting = 0;
rtb->num_retransmittable = 0;
+ rtb->num_pto_eliciting = 0;
rtb->probe_pkt_left = 0;
rtb->pktns_id = pktns_id;
rtb->cc_pkt_num = 0;
rtb->cc_bytes_in_flight = 0;
rtb->persistent_congestion_start_ts = UINT64_MAX;
rtb->num_lost_pkts = 0;
+ rtb->num_lost_pmtud_pkts = 0;
}
void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
@@ -243,7 +332,9 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
it = ngtcp2_ksl_begin(&rtb->ents);
for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
- ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem);
+ ngtcp2_rtb_entry_objalloc_del(ngtcp2_ksl_it_get(&it),
+ rtb->rtb_entry_objalloc, rtb->frc_objalloc,
+ rtb->mem);
}
ngtcp2_ksl_free(&rtb->ents);
@@ -266,14 +357,23 @@ static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
++rtb->num_retransmittable;
}
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+ ++rtb->num_pto_eliciting;
+ }
}
-static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
- ngtcp2_conn_stat *cstat) {
+static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat) {
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
assert(rtb->num_lost_pkts);
--rtb->num_lost_pkts;
- return;
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ assert(rtb->num_lost_pmtud_pkts);
+ --rtb->num_lost_pmtud_pkts;
+ }
+
+ return 0;
}
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
@@ -287,22 +387,44 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
--rtb->num_retransmittable;
}
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+ assert(rtb->num_pto_eliciting);
+ --rtb->num_pto_eliciting;
+ }
+
if (rtb->cc_pkt_num <= ent->hd.pkt_num) {
assert(cstat->bytes_in_flight >= ent->pktlen);
cstat->bytes_in_flight -= ent->pktlen;
assert(rtb->cc_bytes_in_flight >= ent->pktlen);
rtb->cc_bytes_in_flight -= ent->pktlen;
+
+ /* If PMTUD packet is lost, we do not report the lost bytes to the
+ caller in order to ignore loss of PMTUD packet. */
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ return 0;
+ }
+
+ return ent->pktlen;
}
+
+ return 0;
}
+/* NGTCP2_RECLAIM_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_RECLAIM_FLAG_NONE 0x00u
+/* NGTCP2_RECLAIM_FLAG_ON_LOSS indicates that frames are reclaimed
+ because of the packet loss.*/
+#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01u
+
/*
* rtb_reclaim_frame queues unacknowledged frames included in |ent|
* for retransmission. The re-queued frames are not deleted from
- * |ent|. It returns the number of frames queued.
+ * |ent|. It returns the number of frames queued. |flags| is bitwise
+ * OR of 0 or more of NGTCP2_RECLAIM_FLAG_*.
*/
-static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
- ngtcp2_pktns *pktns,
+static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
ngtcp2_rtb_entry *ent) {
ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq;
ngtcp2_frame *fr;
@@ -310,9 +432,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
ngtcp2_range gap, range;
size_t num_reclaimed = 0;
int rv;
+ int streamfrq_empty;
+
+ assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE);
- /* PADDING only (or PADDING + ACK ) packets will have NULL
- ent->frc. */
/* TODO Reconsider the order of pfrc */
for (frc = ent->frc; frc; frc = frc->next) {
fr = &frc->fr;
@@ -334,13 +457,28 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
range.end = fr->stream.offset +
ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt);
range = ngtcp2_range_intersect(&range, &gap);
- if (ngtcp2_range_len(&range) == 0 &&
- (!fr->stream.fin || (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED))) {
- continue;
+ if (ngtcp2_range_len(&range) == 0) {
+ if (!fr->stream.fin) {
+ /* 0 length STREAM frame with offset == 0 must be
+ retransmitted if no non-empty data is sent to this stream
+ and no data in this stream is acknowledged. */
+ if (fr->stream.offset != 0 || fr->stream.datacnt != 0 ||
+ strm->tx.offset || (strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) {
+ continue;
+ }
+ } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) {
+ continue;
+ }
+ }
+
+ if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) &&
+ ent->hd.pkt_num != strm->tx.last_lost_pkt_num) {
+ strm->tx.last_lost_pkt_num = ent->hd.pkt_num;
+ ++strm->tx.loss_count;
}
- rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt,
- rtb->mem);
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem);
if (rv != 0) {
return rv;
}
@@ -349,9 +487,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data,
fr->stream.datacnt);
+ streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
rv = ngtcp2_strm_streamfrq_push(strm, nfrc);
if (rv != 0) {
- ngtcp2_frame_chain_del(nfrc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem);
return rv;
}
if (!ngtcp2_strm_is_tx_queued(strm)) {
@@ -361,6 +500,9 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
return rv;
}
}
+ if (streamfrq_empty) {
+ ++conn->tx.strmq_nretrans;
+ }
++num_reclaimed;
@@ -378,8 +520,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
continue;
}
- rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt,
- rtb->mem);
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, fr->crypto.datacnt, rtb->frc_objalloc, rtb->mem);
if (rv != 0) {
return rv;
}
@@ -392,7 +534,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
&nfrc->fr.crypto.offset, nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(nfrc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem);
return rv;
}
@@ -400,8 +542,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
continue;
case NGTCP2_FRAME_NEW_TOKEN:
- rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token,
- rtb->mem);
+ rv = ngtcp2_frame_chain_new_token_objalloc_new(
+ &nfrc, &fr->new_token.token, rtb->frc_objalloc, rtb->mem);
if (rv != 0) {
return rv;
}
@@ -412,8 +554,11 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
}
break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ continue;
default:
- rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, rtb->frc_objalloc);
if (rv != 0) {
return rv;
}
@@ -438,11 +583,41 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
return (ngtcp2_ssize)num_reclaimed;
}
+/*
+ * conn_process_lost_datagram calls ngtcp2_lost_datagram callback for
+ * lost DATAGRAM frames.
+ */
+static int conn_process_lost_datagram(ngtcp2_conn *conn,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_frame_chain *frc;
+ int rv;
+
+ for (frc = ent->frc; frc; frc = frc->next) {
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ assert(conn->callbacks.lost_datagram);
+
+ rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
- ngtcp2_rtb_entry *ent, ngtcp2_conn *conn,
- ngtcp2_pktns *pktns, ngtcp2_tstamp ts) {
+ ngtcp2_rtb_entry *ent, ngtcp2_conn_stat *cstat,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_tstamp ts) {
int rv;
ngtcp2_ssize reclaimed;
+ ngtcp2_cc *cc = rtb->cc;
+ ngtcp2_cc_pkt pkt;
ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
ent->ts);
@@ -451,55 +626,60 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
}
- if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)) {
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
- "pkn=%" PRId64 " has already been reclaimed on PTO",
- ent->hd.pkt_num);
- assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
- assert(UINT64_MAX == ent->lost_ts);
-
- ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
- ent->lost_ts = ts;
-
- ++rtb->num_lost_pkts;
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ ++rtb->num_lost_pmtud_pkts;
+ } else if (rtb->cc->on_pkt_lost) {
+ cc->on_pkt_lost(cc, cstat,
+ ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
+ rtb->pktns_id, ent->ts, ent->rst.lost,
+ ent->rst.tx_in_flight,
+ ent->rst.is_app_limited),
+ ts);
+ }
- ngtcp2_ksl_it_next(it);
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " has already been reclaimed on PTO",
+ ent->hd.pkt_num);
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+ assert(UINT64_MAX == ent->lost_ts);
- return 0;
- }
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = ts;
- if (ent->frc) {
- assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
- assert(UINT64_MAX == ent->lost_ts);
+ ++rtb->num_lost_pkts;
- reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
- if (reclaimed < 0) {
- return (int)reclaimed;
- }
+ ngtcp2_ksl_it_next(it);
- if (reclaimed) {
- ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
- ent->lost_ts = ts;
+ return 0;
+ }
- ++rtb->num_lost_pkts;
+ if (conn->callbacks.lost_datagram &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) {
+ rv = conn_process_lost_datagram(conn, ent);
+ if (rv != 0) {
+ return rv;
+ }
+ }
- ngtcp2_ksl_it_next(it);
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
+ assert(ent->frc);
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+ assert(UINT64_MAX == ent->lost_ts);
- return 0;
- }
+ reclaimed =
+ rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_ON_LOSS, conn, pktns, ent);
+ if (reclaimed < 0) {
+ return (int)reclaimed;
}
- } else {
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
- "pkn=%" PRId64
- " is a probe packet, no retransmission is necessary",
- ent->hd.pkt_num);
}
- rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num);
- assert(0 == rv);
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = ts;
- ngtcp2_rtb_entry_del(ent, rtb->mem);
+ ++rtb->num_lost_pkts;
+
+ ngtcp2_ksl_it_next(it);
return 0;
}
@@ -526,7 +706,9 @@ static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent,
ngtcp2_conn_stat *cstat) {
int rv;
- rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num);
+ (void)rv;
+
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num);
assert(0 == rv);
rtb_on_remove(rtb, ent, cstat);
@@ -535,6 +717,35 @@ static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
ngtcp2_list_insert(ent, pent);
}
+static void conn_ack_crypto_data(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ uint64_t datalen) {
+ ngtcp2_buf_chain **pbufchain, *bufchain;
+ size_t left;
+
+ for (pbufchain = &pktns->crypto.tx.data; *pbufchain;) {
+ left = ngtcp2_buf_len(&(*pbufchain)->buf);
+ if (left > datalen) {
+ (*pbufchain)->buf.pos += datalen;
+ return;
+ }
+
+ bufchain = *pbufchain;
+ *pbufchain = bufchain->next;
+
+ ngtcp2_mem_free(conn->mem, bufchain);
+
+ datalen -= left;
+
+ if (datalen == 0) {
+ return;
+ }
+ }
+
+ assert(datalen == 0);
+
+ return;
+}
+
static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
ngtcp2_conn *conn) {
ngtcp2_frame_chain *frc;
@@ -543,7 +754,19 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
int rv;
uint64_t datalen;
ngtcp2_strm *crypto = rtb->crypto;
- ngtcp2_crypto_level crypto_level;
+ ngtcp2_pktns *pktns = NULL;
+
+ if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && conn->pmtud &&
+ conn->pmtud->tx_pkt_num <= ent->hd.pkt_num) {
+ ngtcp2_pmtud_probe_success(conn->pmtud, ent->pktlen);
+
+ conn->dcid.current.max_udp_payload_size =
+ ngtcp2_max(conn->dcid.current.max_udp_payload_size, ent->pktlen);
+
+ if (ngtcp2_pmtud_finished(conn->pmtud)) {
+ ngtcp2_conn_stop_pmtud(conn);
+ }
+ }
for (frc = ent->frc; frc; frc = frc->next) {
if (frc->binder) {
@@ -557,6 +780,8 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
break;
}
+ strm->flags |= NGTCP2_STRM_FLAG_ANY_ACKED;
+
if (frc->fr.stream.fin) {
strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED;
}
@@ -584,7 +809,7 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
}
}
- rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR);
+ rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
if (rv != 0) {
return rv;
}
@@ -598,33 +823,28 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
return rv;
}
- if (conn->callbacks.acked_crypto_offset) {
- stream_offset = ngtcp2_strm_get_acked_offset(crypto);
- datalen = stream_offset - prev_stream_offset;
- if (datalen == 0) {
- break;
- }
-
- switch (rtb->pktns_id) {
- case NGTCP2_PKTNS_ID_INITIAL:
- crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL;
- break;
- case NGTCP2_PKTNS_ID_HANDSHAKE:
- crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
- break;
- case NGTCP2_PKTNS_ID_APPLICATION:
- crypto_level = NGTCP2_CRYPTO_LEVEL_APPLICATION;
- break;
- default:
- assert(0);
- }
+ stream_offset = ngtcp2_strm_get_acked_offset(crypto);
+ datalen = stream_offset - prev_stream_offset;
+ if (datalen == 0) {
+ break;
+ }
- rv = conn->callbacks.acked_crypto_offset(
- conn, crypto_level, prev_stream_offset, datalen, conn->user_data);
- if (rv != 0) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
+ switch (rtb->pktns_id) {
+ case NGTCP2_PKTNS_ID_INITIAL:
+ pktns = conn->in_pktns;
+ break;
+ case NGTCP2_PKTNS_ID_HANDSHAKE:
+ pktns = conn->hs_pktns;
+ break;
+ case NGTCP2_PKTNS_ID_APPLICATION:
+ pktns = &conn->pktns;
+ break;
+ default:
+ assert(0);
}
+
+ conn_ack_crypto_data(conn, pktns, datalen);
+
break;
case NGTCP2_FRAME_RESET_STREAM:
strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id);
@@ -632,14 +852,26 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
break;
}
strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED;
- rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR);
+ rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
if (rv != 0) {
return rv;
}
break;
case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
- assert(conn->dcid.num_retire_queued);
- --conn->dcid.num_retire_queued;
+ ngtcp2_conn_untrack_retired_dcid_seq(conn,
+ frc->fr.retire_connection_id.seq);
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ if (!conn->callbacks.ack_datagram) {
+ break;
+ }
+
+ rv = conn->callbacks.ack_datagram(conn, frc->fr.datagram.dgram_id,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
break;
}
}
@@ -655,7 +887,9 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
cc->on_pkt_acked(cc, cstat,
ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
- rtb->pktns_id, ent->ts),
+ rtb->pktns_id, ent->ts, ent->rst.lost,
+ ent->rst.tx_in_flight,
+ ent->rst.is_app_limited),
ts);
if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) &&
@@ -705,6 +939,10 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
}
}
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
ngtcp2_conn_stat *cstat, ngtcp2_conn *conn,
ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts,
@@ -723,10 +961,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
int ack_eliciting_pkt_acked = 0;
size_t ecn_acked = 0;
int verify_ecn = 0;
+ ngtcp2_cc_ack cc_ack = {0};
+ size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts;
+
+ cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight;
+ cc_ack.rtt = UINT64_MAX;
if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) &&
+ (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) &&
largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) {
- conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+ conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED |
+ NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR);
conn->crypto.key_update.confirmed_ts = ts;
ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
@@ -740,7 +985,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
/* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack);
if (ngtcp2_ksl_it_end(&it)) {
- if (verify_ecn) {
+ if (conn && verify_ecn) {
conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
largest_acked_sent_ts, ts);
}
@@ -800,15 +1045,14 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
}
if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
- ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts,
- fr->ack_delay_unscaled, ts);
- if (cc->new_rtt_sample) {
+ cc_ack.rtt = pkt_ts - largest_pkt_sent_ts;
+
+ rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts);
+ if (rv == 0 && cc->new_rtt_sample) {
cc->new_rtt_sample(cc, cstat, ts);
}
}
- ngtcp2_rst_on_ack_recv(rtb->rst, cstat);
-
if (conn) {
for (ent = acked_ent; ent; ent = acked_ent) {
if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num &&
@@ -826,9 +1070,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
goto fail;
}
+ if (ent->hd.pkt_num >= rtb->cc_pkt_num) {
+ assert(cc_ack.pkt_delivered <= ent->rst.delivered);
+
+ cc_ack.bytes_delivered += ent->pktlen;
+ cc_ack.pkt_delivered = ent->rst.delivered;
+ }
+
rtb_on_pkt_acked(rtb, ent, cstat, ts);
acked_ent = ent->next;
- ngtcp2_rtb_entry_del(ent, rtb->mem);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
}
if (verify_ecn) {
@@ -840,29 +1092,48 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
for (ent = acked_ent; ent; ent = acked_ent) {
rtb_on_pkt_acked(rtb, ent, cstat, ts);
acked_ent = ent->next;
- ngtcp2_rtb_entry_del(ent, rtb->mem);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ }
+ }
+
+ if (rtb->cc->on_spurious_congestion && num_lost_pkts &&
+ rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts == 0) {
+ rtb->cc->on_spurious_congestion(cc, cstat, ts);
+ }
+
+ ngtcp2_rst_on_ack_recv(rtb->rst, cstat, cc_ack.pkt_delivered);
+
+ if (conn && num_acked > 0) {
+ rv = rtb_detect_lost_pkt(rtb, &cc_ack.bytes_lost, conn, pktns, cstat, ts);
+ if (rv != 0) {
+ return rv;
}
}
- cc->on_ack_recv(cc, cstat, ts);
+ rtb->rst->lost += cc_ack.bytes_lost;
+
+ cc_ack.largest_acked_sent_ts = largest_acked_sent_ts;
+ cc->on_ack_recv(cc, cstat, &cc_ack, ts);
return num_acked;
fail:
for (ent = acked_ent; ent; ent = acked_ent) {
acked_ent = ent->next;
- ngtcp2_rtb_entry_del(ent, rtb->mem);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
}
return rv;
}
static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat,
- const ngtcp2_rtb_entry *ent, uint64_t loss_delay,
- ngtcp2_tstamp lost_send_time, uint64_t pkt_thres) {
+ const ngtcp2_rtb_entry *ent, ngtcp2_duration loss_delay,
+ size_t pkt_thres, ngtcp2_tstamp ts) {
ngtcp2_tstamp loss_time;
- if (ent->ts <= lost_send_time ||
+ if (ent->ts + loss_delay <= ts ||
rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) {
return 1;
}
@@ -906,12 +1177,11 @@ static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) {
pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost;
}
-int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
- ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat,
- ngtcp2_duration pto, ngtcp2_tstamp ts) {
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
ngtcp2_rtb_entry *ent;
ngtcp2_duration loss_delay;
- ngtcp2_tstamp lost_send_time;
ngtcp2_ksl_it it;
ngtcp2_tstamp latest_ts, oldest_ts;
int64_t last_lost_pkt_num;
@@ -922,11 +1192,14 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
rtb->cc_bytes_in_flight / cstat->max_udp_payload_size / 2;
size_t ecn_pkt_lost = 0;
ngtcp2_tstamp start_ts;
+ ngtcp2_duration pto = ngtcp2_conn_compute_pto(conn, pktns);
+ uint64_t bytes_lost = 0;
+ ngtcp2_duration max_ack_delay;
pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD);
+ pkt_thres = ngtcp2_min(pkt_thres, 256);
cstat->loss_time[rtb->pktns_id] = UINT64_MAX;
loss_delay = compute_pkt_loss_delay(cstat);
- lost_send_time = ts - loss_delay;
it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num);
for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
@@ -936,15 +1209,18 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
break;
}
- if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, lost_send_time, pkt_thres)) {
+ if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, (size_t)pkt_thres, ts)) {
/* All entries from ent are considered to be lost. */
latest_ts = oldest_ts = ent->ts;
last_lost_pkt_num = ent->hd.pkt_num;
+ max_ack_delay = conn->remote.transport_params
+ ? conn->remote.transport_params->max_ack_delay
+ : 0;
- congestion_period = (cstat->smoothed_rtt +
- ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) +
- conn->remote.transport_params.max_ack_delay) *
- NGTCP2_PERSISTENT_CONGESTION_THRESHOLD;
+ congestion_period =
+ (cstat->smoothed_rtt +
+ ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) + max_ack_delay) *
+ NGTCP2_PERSISTENT_CONGESTION_THRESHOLD;
start_ts = ngtcp2_max(rtb->persistent_congestion_start_ts,
cstat->first_rtt_sample_ts);
@@ -974,13 +1250,19 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
++ecn_pkt_lost;
}
- rtb_on_remove(rtb, ent, cstat);
- rv = rtb_on_pkt_lost(rtb, &it, ent, conn, pktns, ts);
+ bytes_lost += rtb_on_remove(rtb, ent, cstat);
+ rv = rtb_on_pkt_lost(rtb, &it, ent, cstat, conn, pktns, ts);
if (rv != 0) {
return rv;
}
}
+ /* If only PMTUD packets are lost, do not trigger congestion
+ event. */
+ if (bytes_lost == 0) {
+ break;
+ }
+
switch (conn->tx.ecn.state) {
case NGTCP2_ECN_STATE_TESTING:
if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
@@ -1038,15 +1320,27 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
}
}
- ngtcp2_rtb_remove_excessive_lost_pkt(rtb, pkt_thres);
+ ngtcp2_rtb_remove_excessive_lost_pkt(rtb, (size_t)pkt_thres);
+
+ if (ppkt_lost) {
+ *ppkt_lost = bytes_lost;
+ }
return 0;
}
+int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ return rtb_detect_lost_pkt(rtb, /* ppkt_lost = */ NULL, conn, pktns, cstat,
+ ts);
+}
+
void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
ngtcp2_ksl_it it = ngtcp2_ksl_end(&rtb->ents);
ngtcp2_rtb_entry *ent;
int rv;
+ (void)rv;
for (; rtb->num_lost_pkts > n;) {
assert(ngtcp2_ksl_it_end(&it));
@@ -1059,9 +1353,15 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
"removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
--rtb->num_lost_pkts;
- rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ --rtb->num_lost_pmtud_pkts;
+ }
+
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
assert(0 == rv);
- ngtcp2_rtb_entry_del(ent, rtb->mem);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
}
}
@@ -1070,6 +1370,7 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
ngtcp2_ksl_it it;
ngtcp2_rtb_entry *ent;
int rv;
+ (void)rv;
if (ngtcp2_ksl_len(&rtb->ents) == 0) {
return;
@@ -1092,9 +1393,15 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
"removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
--rtb->num_lost_pkts;
- rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ --rtb->num_lost_pmtud_pkts;
+ }
+
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
assert(0 == rv);
- ngtcp2_rtb_entry_del(ent, rtb->mem);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
if (ngtcp2_ksl_len(&rtb->ents) == 0) {
return;
@@ -1128,6 +1435,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
ngtcp2_stream *sfr;
ngtcp2_strm *strm;
int rv;
+ int streamfrq_empty;
ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
ent->ts);
@@ -1144,19 +1452,21 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
return 0;
}
- if (!ent->frc) {
- /* PADDING only (or PADDING + ACK ) packets will have NULL
- ent->frc. */
- assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
- assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED));
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64
+ " is a PMTUD probe packet, no retransmission is necessary",
+ ent->hd.pkt_num);
return 0;
}
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
--rtb->num_lost_pkts;
- }
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ --rtb->num_lost_pmtud_pkts;
+ }
+
ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
"pkn=%" PRId64
" was declared lost and has already been retransmitted",
@@ -1171,6 +1481,14 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
return 0;
}
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) &&
+ (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) ||
+ !conn->callbacks.lost_datagram)) {
+ /* PADDING only (or PADDING + ACK ) packets will have NULL
+ ent->frc. */
+ return 0;
+ }
+
pfrc = &ent->frc;
for (; *pfrc;) {
@@ -1184,12 +1502,13 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
if (!strm) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
break;
}
+ streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
rv = ngtcp2_strm_streamfrq_push(strm, frc);
if (rv != 0) {
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
return rv;
}
if (!ngtcp2_strm_is_tx_queued(strm)) {
@@ -1199,6 +1518,9 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
return rv;
}
}
+ if (streamfrq_empty) {
+ ++conn->tx.strmq_nretrans;
+ }
break;
case NGTCP2_FRAME_CRYPTO:
frc = *pfrc;
@@ -1210,10 +1532,26 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
&frc->fr.crypto.offset, frc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
return rv;
}
break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ frc = *pfrc;
+
+ if (conn->callbacks.lost_datagram) {
+ rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ *pfrc = (*pfrc)->next;
+
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
+ break;
default:
pfrc = &(*pfrc)->next;
}
@@ -1238,11 +1576,12 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
ent = ngtcp2_ksl_it_get(&it);
rtb_on_remove(rtb, ent, cstat);
- rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num);
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
assert(0 == rv);
rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent);
- ngtcp2_rtb_entry_del(ent, rtb->mem);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
if (rv != 0) {
return rv;
}
@@ -1251,6 +1590,31 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
return 0;
}
+void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat) {
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ksl_it it;
+ int rv;
+ (void)rv;
+
+ it = ngtcp2_ksl_begin(&rtb->ents);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (ent->hd.type != NGTCP2_PKT_0RTT) {
+ ngtcp2_ksl_it_next(&it);
+ continue;
+ }
+
+ rtb_on_remove(rtb, ent, cstat);
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
+ assert(0 == rv);
+
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ }
+}
+
int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) {
return ngtcp2_ksl_len(&rtb->ents) == 0;
}
@@ -1280,7 +1644,8 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
assert(ent->frc);
- reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
+ reclaimed =
+ rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_NONE, conn, pktns, ent);
if (reclaimed < 0) {
return reclaimed;
}
@@ -1292,6 +1657,12 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
assert(rtb->num_retransmittable);
--rtb->num_retransmittable;
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+ ent->flags &= (uint16_t)~NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING;
+ assert(rtb->num_pto_eliciting);
+ --rtb->num_pto_eliciting;
+ }
+
if (reclaimed) {
--num_pkts;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h
index 70f43ffd924..a97805dbaf3 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h
@@ -34,6 +34,7 @@
#include "ngtcp2_pkt.h"
#include "ngtcp2_ksl.h"
#include "ngtcp2_pq.h"
+#include "ngtcp2_objalloc.h"
typedef struct ngtcp2_conn ngtcp2_conn;
typedef struct ngtcp2_pktns ngtcp2_pktns;
@@ -41,13 +42,14 @@ typedef struct ngtcp2_log ngtcp2_log;
typedef struct ngtcp2_qlog ngtcp2_qlog;
typedef struct ngtcp2_strm ngtcp2_strm;
typedef struct ngtcp2_rst ngtcp2_rst;
+typedef struct ngtcp2_cc ngtcp2_cc;
/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is
set. */
-#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00
+#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00u
/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information
which a frame carries has been acknowledged. */
-#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01
+#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01u
/*
* ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to
@@ -71,11 +73,19 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
* ngtcp2_frame_chain chains frames in a single packet.
*/
struct ngtcp2_frame_chain {
- ngtcp2_frame_chain *next;
- ngtcp2_frame_chain_binder *binder;
- ngtcp2_frame fr;
+ union {
+ struct {
+ ngtcp2_frame_chain *next;
+ ngtcp2_frame_chain_binder *binder;
+ ngtcp2_frame fr;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
};
+ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent);
+
/*
* ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using
* new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL
@@ -111,6 +121,13 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem);
/*
+ * ngtcp2_frame_chain_objalloc_new behaves like
+ * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object.
+ */
+int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
+ ngtcp2_objalloc *objalloc);
+
+/*
* ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new,
* but it allocates extra memory |extralen| in order to extend
* ngtcp2_frame.
@@ -119,30 +136,33 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
const ngtcp2_mem *mem);
/*
- * ngtcp2_frame_chain_stream_datacnt_new works like
+ * ngtcp2_frame_chain_stream_datacnt_objalloc_new works like
* ngtcp2_frame_chain_new, but it allocates enough data to store
* additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream
- * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called
- * internally.
+ * object. If no additional space is required,
+ * ngtcp2_frame_chain_objalloc_new is called internally.
*/
-int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc,
- size_t datacnt,
- const ngtcp2_mem *mem);
+int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
/*
- * ngtcp2_frame_chain_crypto_datacnt_new works like
+ * ngtcp2_frame_chain_crypto_datacnt_objalloc_new works like
* ngtcp2_frame_chain_new, but it allocates enough data to store
* additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto
- * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called
- * internally.
+ * object. If no additional space is required,
+ * ngtcp2_frame_chain_objalloc_new is called internally.
*/
-int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc,
- size_t datacnt,
- const ngtcp2_mem *mem);
+int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
-int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
- const ngtcp2_vec *token,
- const ngtcp2_mem *mem);
+int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
+ const ngtcp2_vec *token,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
/*
* ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the
@@ -151,42 +171,61 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem);
/*
+ * ngtcp2_frame_chain_objalloc_del adds |frc| to |objalloc| for reuse.
+ * It might just delete |frc| depending on the frame type and the size
+ * of |frc|.
+ */
+void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
+
+/*
* ngtcp2_frame_chain_init initializes |frc|.
*/
void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc);
/*
- * ngtcp2_frame_chain_list_del deletes |frc|, and all objects
- * connected by next field.
+ * ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain
+ * linked from |frc| to |objalloc| for reuse. Depending on the frame type
+ * and its size, ngtcp2_frame_chain might be deleted instead.
*/
-void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc,
- const ngtcp2_mem *mem);
+void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
/* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00
+#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00u
/* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a
probe packet. */
-#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01
+#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01u
/* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry
includes a frame which must be retransmitted until it is
acknowledged. In most cases, this flag is used along with
- NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING. We have these 2 flags because
- NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE triggers PTO, but just
- NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING does not. */
-#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING and
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */
+#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02u
/* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry
elicits acknowledgement. */
-#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04
+#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04u
/* NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED indicates that the packet has
been reclaimed on PTO. It is not marked lost yet and still
consumes congestion window. */
-#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08
+#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08u
/* NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED indicates that the entry
- has been marked lost and scheduled to retransmit. */
-#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10
+ has been marked lost and, optionally, scheduled to retransmit. */
+#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10u
/* NGTCP2_RTB_ENTRY_FLAG_ECN indicates that the entry is included in a
UDP datagram with ECN marking. */
-#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20
+#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20u
+/* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes
+ DATAGRAM frame. */
+#define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40u
+/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes
+ a PMTUD probe packet. */
+#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80u
+/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry
+ includes a packet which elicits PTO probe packets. */
+#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u
typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
@@ -195,58 +234,69 @@ typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
* to the one packet which is waiting for its ACK.
*/
struct ngtcp2_rtb_entry {
- ngtcp2_rtb_entry *next;
-
- struct {
- int64_t pkt_num;
- uint8_t type;
- uint8_t flags;
- } hd;
- ngtcp2_frame_chain *frc;
- /* ts is the time point when a packet included in this entry is sent
- to a peer. */
- ngtcp2_tstamp ts;
- /* lost_ts is the time when this entry is marked lost. */
- ngtcp2_tstamp lost_ts;
- /* pktlen is the length of QUIC packet */
- size_t pktlen;
- struct {
- uint64_t delivered;
- ngtcp2_tstamp delivered_ts;
- ngtcp2_tstamp first_sent_ts;
- int is_app_limited;
- } rst;
- /* flags is bitwise-OR of zero or more of
- NGTCP2_RTB_ENTRY_FLAG_*. */
- uint8_t flags;
+ union {
+ struct {
+ ngtcp2_rtb_entry *next;
+
+ struct {
+ int64_t pkt_num;
+ uint8_t type;
+ uint8_t flags;
+ } hd;
+ ngtcp2_frame_chain *frc;
+ /* ts is the time point when a packet included in this entry is sent
+ to a peer. */
+ ngtcp2_tstamp ts;
+ /* lost_ts is the time when this entry is marked lost. */
+ ngtcp2_tstamp lost_ts;
+ /* pktlen is the length of QUIC packet */
+ size_t pktlen;
+ struct {
+ uint64_t delivered;
+ ngtcp2_tstamp delivered_ts;
+ ngtcp2_tstamp first_sent_ts;
+ uint64_t tx_in_flight;
+ uint64_t lost;
+ int is_app_limited;
+ } rst;
+ /* flags is bitwise-OR of zero or more of
+ NGTCP2_RTB_ENTRY_FLAG_*. */
+ uint16_t flags;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
};
+ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent);
+
/*
* ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns
- * its pointer to |*pent|. On success, |*pent| takes ownership of
- * |frc|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
+ * its pointer to |*pent|.
*/
-int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd,
- ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
- size_t pktlen, uint8_t flags, const ngtcp2_mem *mem);
+int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
+ const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+ size_t pktlen, uint16_t flags,
+ ngtcp2_objalloc *objalloc);
/*
- * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory
- * pointed by |ent|.
+ * ngtcp2_rtb_entry_objalloc_del adds |ent| to |objalloc| for reuse.
+ * ngtcp2_frame_chain linked from ent->frc are also added to
+ * |frc_objalloc| depending on their frame type and size.
*/
-void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem);
+void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent,
+ ngtcp2_objalloc *objalloc,
+ ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem);
/*
* ngtcp2_rtb tracks sent packets, and its ACK timeout for
* retransmission.
*/
typedef struct ngtcp2_rtb {
+ ngtcp2_objalloc *frc_objalloc;
+ ngtcp2_objalloc *rtb_entry_objalloc;
/* ents includes ngtcp2_rtb_entry sorted by decreasing order of
packet number. */
ngtcp2_ksl ents;
@@ -265,6 +315,9 @@ typedef struct ngtcp2_rtb {
/* num_retransmittable is the number of packets which contain frames
that must be retransmitted on loss. */
size_t num_retransmittable;
+ /* num_pto_eliciting is the number of packets that elicit PTO probe
+ packets. */
+ size_t num_pto_eliciting;
/* probe_pkt_left is the number of probe packet to send */
size_t probe_pkt_left;
/* pktns_id is the identifier of packet number space. */
@@ -283,6 +336,10 @@ typedef struct ngtcp2_rtb {
/* num_lost_pkts is the number entries in ents which has
NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */
size_t num_lost_pkts;
+ /* num_lost_pmtud_pkts is the number of entries in ents which have
+ both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and
+ NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */
+ size_t num_lost_pmtud_pkts;
} ngtcp2_rtb;
/*
@@ -290,7 +347,9 @@ typedef struct ngtcp2_rtb {
*/
void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc,
- ngtcp2_log *log, ngtcp2_qlog *qlog, const ngtcp2_mem *mem);
+ ngtcp2_log *log, ngtcp2_qlog *qlog,
+ ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem);
/*
* ngtcp2_rtb_free deallocates resources allocated for |rtb|.
@@ -344,7 +403,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
*/
int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat,
- ngtcp2_duration pto, ngtcp2_tstamp ts);
+ ngtcp2_tstamp ts);
/*
* ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet.
@@ -368,6 +427,11 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat);
/*
+ * ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets.
+ */
+void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat);
+
+/*
* ngtcp2_rtb_empty returns nonzero if |rtb| have no entry.
*/
int ngtcp2_rtb_empty(ngtcp2_rtb *rtb);
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c
index 3118955b248..c1ce64a2e57 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c
@@ -216,20 +216,6 @@ uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) {
return dest;
}
-int ngtcp2_verify_stateless_reset_token(const uint8_t *want,
- const uint8_t *got) {
- return !ngtcp2_check_invalid_stateless_reset_token(got) &&
- ngtcp2_cmemeq(want, got, NGTCP2_STATELESS_RESET_TOKENLEN)
- ? 0
- : NGTCP2_ERR_INVALID_ARGUMENT;
-}
-
-int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token) {
- static uint8_t invalid_token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0};
-
- return 0 == memcmp(invalid_token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
-}
-
int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) {
size_t i;
int rv = 0;
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h
index bd0145747c8..04735d6dec5 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h
@@ -78,25 +78,6 @@ char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
size_t len);
/*
- * ngtcp2_verify_stateless_reset_token verifies stateless reset token
- * |want| and |got|. This function returns 0 if |want| equals |got|
- * and |got| is not all zero, or one of the following negative error
- * codes:
- *
- * NGTCP2_ERR_INVALID_ARGUMENT
- * Token does not match; or token is all zero.
- */
-int ngtcp2_verify_stateless_reset_token(const uint8_t *want,
- const uint8_t *got);
-
-/*
- * ngtcp2_check_invalid_stateless_reset_token returns nonzero if
- * |token| is invalid stateless reset token. Currently, token which
- * consists of all zeros is considered invalid.
- */
-int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token);
-
-/*
* ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers
* pointed by |a| and |b| are equal. The comparison is done in a
* constant time manner.
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c
index 8e8eef0c9c9..6f20e866ad5 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c
@@ -35,9 +35,11 @@ static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
return *(int64_t *)lhs < *(int64_t *)rhs;
}
-int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
- uint64_t max_rx_offset, uint64_t max_tx_offset,
- void *stream_user_data, const ngtcp2_mem *mem) {
+void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
+ uint64_t max_rx_offset, uint64_t max_tx_offset,
+ void *stream_user_data, ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem) {
+ strm->frc_objalloc = frc_objalloc;
strm->cycle = 0;
strm->tx.acked_offset = NULL;
strm->tx.cont_acked_offset = 0;
@@ -45,6 +47,8 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
strm->tx.offset = 0;
strm->tx.max_offset = max_tx_offset;
strm->tx.last_max_stream_data_ts = UINT64_MAX;
+ strm->tx.loss_count = 0;
+ strm->tx.last_lost_pkt_num = -1;
strm->rx.rob = NULL;
strm->rx.cont_offset = 0;
strm->rx.last_offset = 0;
@@ -53,12 +57,9 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
strm->stream_user_data = stream_user_data;
strm->rx.window = strm->rx.max_offset = strm->rx.unsent_max_offset =
max_rx_offset;
- strm->me.key = (uint64_t)stream_id;
strm->pe.index = NGTCP2_PQ_BAD_INDEX;
strm->mem = mem;
strm->app_error_code = 0;
-
- return 0;
}
void ngtcp2_strm_free(ngtcp2_strm *strm) {
@@ -71,17 +72,23 @@ void ngtcp2_strm_free(ngtcp2_strm *strm) {
if (strm->tx.streamfrq) {
for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
ngtcp2_ksl_it_next(&it)) {
- ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem);
+ ngtcp2_frame_chain_objalloc_del(ngtcp2_ksl_it_get(&it),
+ strm->frc_objalloc, strm->mem);
}
ngtcp2_ksl_free(strm->tx.streamfrq);
ngtcp2_mem_free(strm->mem, strm->tx.streamfrq);
}
- ngtcp2_rob_free(strm->rx.rob);
- ngtcp2_mem_free(strm->mem, strm->rx.rob);
- ngtcp2_gaptr_free(strm->tx.acked_offset);
- ngtcp2_mem_free(strm->mem, strm->tx.acked_offset);
+ if (strm->rx.rob) {
+ ngtcp2_rob_free(strm->rx.rob);
+ ngtcp2_mem_free(strm->mem, strm->rx.rob);
+ }
+
+ if (strm->tx.acked_offset) {
+ ngtcp2_gaptr_free(strm->tx.acked_offset);
+ ngtcp2_mem_free(strm->mem, strm->tx.acked_offset);
+ }
}
static int strm_rob_init(ngtcp2_strm *strm) {
@@ -155,17 +162,12 @@ void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) {
}
static int strm_streamfrq_init(ngtcp2_strm *strm) {
- int rv;
ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq));
if (streamfrq == NULL) {
return NGTCP2_ERR_NOMEM;
}
- rv = ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem);
- if (rv != 0) {
- ngtcp2_mem_free(strm->mem, streamfrq);
- return rv;
- }
+ ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem);
strm->tx.streamfrq = streamfrq;
@@ -210,7 +212,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
frc = ngtcp2_ksl_it_get(&it);
fr = &frc->fr.stream;
- ngtcp2_ksl_remove(strm->tx.streamfrq, &it, &fr->offset);
+ ngtcp2_ksl_remove_hint(strm->tx.streamfrq, &it, &it, &fr->offset);
idx = 0;
offset = fr->offset;
@@ -234,19 +236,27 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
if (idx == fr->datacnt) {
if (fr->fin) {
if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) {
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0);
return 0;
}
- fr->offset = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt);
+ fr->offset += ngtcp2_vec_len(fr->data, fr->datacnt);
fr->datacnt = 0;
*pfrc = frc;
return 0;
}
- ngtcp2_frame_chain_del(frc, strm->mem);
+
+ if (fr->offset == 0 && fr->datacnt == 0 && strm->tx.offset == 0 &&
+ !(strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) {
+ *pfrc = frc;
+
+ return 0;
+ }
+
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
continue;
}
@@ -285,10 +295,10 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
return 0;
}
- rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx,
- strm->mem);
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, fr->datacnt - end_idx, strm->frc_objalloc, strm->mem);
if (rv != 0) {
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
@@ -310,8 +320,8 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(nfrc, strm->mem);
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
@@ -346,7 +356,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
ngtcp2_frame_chain *frc, *nfrc;
int rv;
size_t nmerged;
- size_t datalen;
+ uint64_t datalen;
ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT];
ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT];
size_t acnt, bcnt;
@@ -375,7 +385,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
*pfrc = NULL;
@@ -393,10 +403,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
assert(acnt > 0);
assert(bcnt > 0);
- rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem);
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, bcnt, strm->frc_objalloc, strm->mem);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
@@ -412,15 +423,16 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(nfrc, strm->mem);
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
- rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem);
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, acnt, strm->frc_objalloc, strm->mem);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
@@ -430,14 +442,14 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
nfr->datacnt = acnt;
ngtcp2_vec_copy(nfr->data, a, acnt);
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
*pfrc = nfrc;
return 0;
}
- left -= datalen;
+ left -= (size_t)datalen;
ngtcp2_vec_copy(a, fr->data, fr->datacnt);
acnt = fr->datacnt;
@@ -452,7 +464,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
rv = strm_streamfrq_unacked_pop(strm, &nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
if (nfrc == NULL) {
@@ -463,7 +475,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
if (nfr->fin && nfr->datacnt == 0) {
fr->fin = 1;
- ngtcp2_frame_chain_del(nfrc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
break;
}
@@ -473,8 +485,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_del(nfrc, strm->mem);
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
break;
@@ -485,7 +497,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
if (nfr->datacnt == 0) {
fr->fin = nfr->fin;
- ngtcp2_frame_chain_del(nfrc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
continue;
}
@@ -493,8 +505,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
- ngtcp2_frame_chain_del(nfrc, strm->mem);
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
@@ -512,9 +524,10 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
assert(acnt > fr->datacnt);
- rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem);
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, acnt, strm->frc_objalloc, strm->mem);
if (rv != 0) {
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
@@ -523,7 +536,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
nfr->datacnt = acnt;
ngtcp2_vec_copy(nfr->data, a, acnt);
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
*pfrc = nfrc;
@@ -535,7 +548,7 @@ uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm) {
ngtcp2_stream *fr;
ngtcp2_range gap;
ngtcp2_ksl_it it;
- size_t datalen;
+ uint64_t datalen;
assert(strm->tx.streamfrq);
assert(ngtcp2_ksl_len(strm->tx.streamfrq));
@@ -589,7 +602,7 @@ void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) {
for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
ngtcp2_ksl_it_next(&it)) {
frc = ngtcp2_ksl_it_get(&it);
- ngtcp2_frame_chain_del(frc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
}
ngtcp2_ksl_clear(strm->tx.streamfrq);
}
@@ -607,9 +620,13 @@ int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) {
strm->tx.offset;
}
+int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm) {
+ return (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) &&
+ ngtcp2_strm_is_all_tx_data_acked(strm);
+}
+
ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm,
uint64_t offset) {
- ngtcp2_ksl_it gapit;
ngtcp2_range gap;
if (strm->tx.acked_offset == NULL) {
@@ -618,8 +635,7 @@ ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm,
return gap;
}
- gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset);
- return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit);
+ return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset);
}
uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) {
@@ -631,7 +647,6 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) {
}
static int strm_acked_offset_init(ngtcp2_strm *strm) {
- int rv;
ngtcp2_gaptr *acked_offset =
ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset));
@@ -639,11 +654,7 @@ static int strm_acked_offset_init(ngtcp2_strm *strm) {
return NGTCP2_ERR_NOMEM;
}
- rv = ngtcp2_gaptr_init(acked_offset, strm->mem);
- if (rv != 0) {
- ngtcp2_mem_free(strm->mem, acked_offset);
- return rv;
- }
+ ngtcp2_gaptr_init(acked_offset, strm->mem);
strm->tx.acked_offset = acked_offset;
@@ -673,3 +684,15 @@ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) {
return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len);
}
+
+void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) {
+ return;
+ }
+
+ assert(0 == strm->app_error_code);
+
+ strm->flags |= NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET;
+ strm->app_error_code = app_error_code;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h
index 6b7418706c7..8e3cfe83543 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h
@@ -40,110 +40,138 @@
typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
/* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_STRM_FLAG_NONE 0x00
+#define NGTCP2_STRM_FLAG_NONE 0x00u
/* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream
data is not allowed. */
-#define NGTCP2_STRM_FLAG_SHUT_RD 0x01
+#define NGTCP2_STRM_FLAG_SHUT_RD 0x01u
/* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of
stream data is not allowed. */
-#define NGTCP2_STRM_FLAG_SHUT_WR 0x02
+#define NGTCP2_STRM_FLAG_SHUT_WR 0x02u
#define NGTCP2_STRM_FLAG_SHUT_RDWR \
(NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR)
/* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is sent from
the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is also
set. */
-#define NGTCP2_STRM_FLAG_SENT_RST 0x04
+#define NGTCP2_STRM_FLAG_SENT_RST 0x04u
/* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is received
from the remote endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_RD
is also set. */
-#define NGTCP2_STRM_FLAG_RECV_RST 0x08
+#define NGTCP2_STRM_FLAG_RECV_RST 0x08u
/* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent
from the local endpoint. */
-#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10
+#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10u
/* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM
is acknowledged by peer. */
-#define NGTCP2_STRM_FLAG_RST_ACKED 0x20
+#define NGTCP2_STRM_FLAG_RST_ACKED 0x20u
/* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit set
is acknowledged by a remote endpoint. */
-#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40
+#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40u
+/* NGTCP2_STRM_FLAG_ANY_ACKED indicates that any portion of stream
+ data, including 0 length segment, is acknowledged. */
+#define NGTCP2_STRM_FLAG_ANY_ACKED 0x80u
+/* NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET indicates that app_error_code
+ field is set. This resolves the ambiguity that the initial
+ app_error_code value 0 might be a proper application error code.
+ In this case, without this flag, we are unable to distinguish
+ assigned value from unassigned one. */
+#define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100u
+/* NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED is set when
+ stream_stop_sending callback is called. */
+#define NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED 0x200u
typedef struct ngtcp2_strm ngtcp2_strm;
struct ngtcp2_strm {
- ngtcp2_map_entry me;
- ngtcp2_pq_entry pe;
- uint64_t cycle;
+ union {
+ struct {
+ ngtcp2_pq_entry pe;
+ uint64_t cycle;
+ ngtcp2_objalloc *frc_objalloc;
- struct {
- /* acked_offset tracks acknowledged outgoing data. */
- ngtcp2_gaptr *acked_offset;
- /* cont_acked_offset is the offset that all data up to this offset
- is acknowledged by a remote endpoint. It is used until the
- remote endpoint acknowledges data in out-of-order. After that,
- acked_offset is used instead. */
- uint64_t cont_acked_offset;
- /* streamfrq contains STREAM frame for retransmission. The flow
- control credits have been paid when they are transmitted first
- time. There are no restriction regarding flow control for
- retransmission. */
- ngtcp2_ksl *streamfrq;
- /* offset is the next offset of outgoing data. In other words, it
- is the number of bytes sent in this stream without
- duplication. */
- uint64_t offset;
- /* max_tx_offset is the maximum offset that local endpoint can
- send for this stream. */
- uint64_t max_offset;
- /* last_max_stream_data_ts is the timestamp when last
- MAX_STREAM_DATA frame is sent. */
- ngtcp2_tstamp last_max_stream_data_ts;
- } tx;
+ struct {
+ /* acked_offset tracks acknowledged outgoing data. */
+ ngtcp2_gaptr *acked_offset;
+ /* cont_acked_offset is the offset that all data up to this offset
+ is acknowledged by a remote endpoint. It is used until the
+ remote endpoint acknowledges data in out-of-order. After that,
+ acked_offset is used instead. */
+ uint64_t cont_acked_offset;
+ /* streamfrq contains STREAM frame for retransmission. The flow
+ control credits have been paid when they are transmitted first
+ time. There are no restriction regarding flow control for
+ retransmission. */
+ ngtcp2_ksl *streamfrq;
+ /* offset is the next offset of outgoing data. In other words, it
+ is the number of bytes sent in this stream without
+ duplication. */
+ uint64_t offset;
+ /* max_tx_offset is the maximum offset that local endpoint can
+ send for this stream. */
+ uint64_t max_offset;
+ /* last_max_stream_data_ts is the timestamp when last
+ MAX_STREAM_DATA frame is sent. */
+ ngtcp2_tstamp last_max_stream_data_ts;
+ /* loss_count is the number of packets that contain STREAM
+ frame for this stream and are declared to be lost. It may
+ include the spurious losses. It does not include a packet
+ whose contents have been reclaimed for PTO and which is
+ later declared to be lost. Those data are not blocked by
+ the flow control and will be sent immediately if no other
+ restrictions are applied. */
+ size_t loss_count;
+ /* last_lost_pkt_num is the packet number of the packet that
+ is counted to loss_count. It is used to avoid to count
+ multiple STREAM frames in one lost packet. */
+ int64_t last_lost_pkt_num;
+ } tx;
- struct {
- /* rob is the reorder buffer for incoming stream data. The data
- received in out of order is buffered and sorted by its offset
- in this object. */
- ngtcp2_rob *rob;
- /* cont_offset is the largest offset of consecutive data. It is
- used until the endpoint receives out-of-order data. After
- that, rob is used to track the offset and data. */
- uint64_t cont_offset;
- /* last_offset is the largest offset of stream data received for
- this stream. */
- uint64_t last_offset;
- /* max_offset is the maximum offset that remote endpoint can send
- to this stream. */
- uint64_t max_offset;
- /* unsent_max_offset is the maximum offset that remote endpoint
- can send to this stream, and it is not notified to the remote
- endpoint. unsent_max_offset >= max_offset must be hold. */
- uint64_t unsent_max_offset;
- /* window is the stream-level flow control window size. */
- uint64_t window;
- } rx;
+ struct {
+ /* rob is the reorder buffer for incoming stream data. The data
+ received in out of order is buffered and sorted by its offset
+ in this object. */
+ ngtcp2_rob *rob;
+ /* cont_offset is the largest offset of consecutive data. It is
+ used until the endpoint receives out-of-order data. After
+ that, rob is used to track the offset and data. */
+ uint64_t cont_offset;
+ /* last_offset is the largest offset of stream data received for
+ this stream. */
+ uint64_t last_offset;
+ /* max_offset is the maximum offset that remote endpoint can send
+ to this stream. */
+ uint64_t max_offset;
+ /* unsent_max_offset is the maximum offset that remote endpoint
+ can send to this stream, and it is not notified to the remote
+ endpoint. unsent_max_offset >= max_offset must be hold. */
+ uint64_t unsent_max_offset;
+ /* window is the stream-level flow control window size. */
+ uint64_t window;
+ } rx;
- const ngtcp2_mem *mem;
- int64_t stream_id;
- void *stream_user_data;
- /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */
- uint32_t flags;
- /* app_error_code is an error code the local endpoint sent in
- RST_STREAM or STOP_SENDING. */
- uint64_t app_error_code;
+ const ngtcp2_mem *mem;
+ int64_t stream_id;
+ void *stream_user_data;
+ /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */
+ uint32_t flags;
+ /* app_error_code is an error code the local endpoint sent in
+ RESET_STREAM or STOP_SENDING, or received from a remote endpoint
+ in RESET_STREAM or STOP_SENDING. First application error code is
+ chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is
+ set in flags field. */
+ uint64_t app_error_code;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
};
/*
* ngtcp2_strm_init initializes |strm|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory
*/
-int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
- uint64_t max_rx_offset, uint64_t max_tx_offset,
- void *stream_user_data, const ngtcp2_mem *mem);
+void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
+ uint64_t max_rx_offset, uint64_t max_tx_offset,
+ void *stream_user_data, ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem);
/*
* ngtcp2_strm_free deallocates memory allocated for |strm|. This
@@ -246,6 +274,13 @@ int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm);
int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm);
/*
+ * ngtcp2_strm_is_all_tx_data_fin_acked behaves like
+ * ngtcp2_strm_is_all_tx_data_acked, but it also requires that STREAM
+ * frame with fin bit set is acknowledged.
+ */
+int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm);
+
+/*
* ngtcp2_strm_get_unacked_range_after returns the range that is not
* acknowledged yet and intersects or comes after |offset|.
*/
@@ -265,4 +300,11 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm);
*/
int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len);
+/*
+ * ngtcp2_strm_set_app_error_code sets |app_error_code| to |strm| and
+ * set NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag. If the flag is
+ * already set, this function does nothing.
+ */
+void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, uint64_t app_error_code);
+
#endif /* NGTCP2_STRM_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c
index 7a6f8afa051..257332e27a2 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c
@@ -61,7 +61,7 @@ void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) {
ngtcp2_mem_free(mem, vec);
}
-size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) {
+uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) {
size_t i;
size_t res = 0;
@@ -72,6 +72,23 @@ size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) {
return res;
}
+int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n) {
+ uint64_t res = 0;
+ size_t len;
+ size_t i;
+
+ for (i = 0; i < n; ++i) {
+ len = vec[i].len;
+ if (len > NGTCP2_MAX_VARINT - res) {
+ return -1;
+ }
+
+ res += len;
+ }
+
+ return (int64_t)res;
+}
+
ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst,
size_t *pdstcnt, size_t left, size_t maxcnt) {
size_t i;
@@ -198,13 +215,10 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
return orig_left - left;
}
-size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten,
- size_t dstcnt, const ngtcp2_vec *src,
- size_t srccnt, size_t left) {
+size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt,
+ const ngtcp2_vec *src, size_t srccnt,
+ size_t left) {
size_t i, j;
- size_t len = left;
-
- *pnwritten = 0;
for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) {
if (src[i].len == 0) {
@@ -214,7 +228,6 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten,
dst[j] = src[i];
if (dst[j].len > left) {
dst[j].len = left;
- *pnwritten = len;
return j + 1;
}
left -= dst[j].len;
@@ -222,8 +235,6 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten,
++j;
}
- *pnwritten = len - left;
-
return j;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h
index 077820a9efe..a39c4392fd2 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h
@@ -65,7 +65,13 @@ void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem);
/*
* ngtcp2_vec_len returns the sum of length in |vec| of |n| elements.
*/
-size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n);
+uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n);
+
+/*
+ * ngtcp2_vec_len_varint is similar to ngtcp2_vec_len, but it returns
+ * -1 if the sum of the length exceeds NGTCP2_MAX_VARINT.
+ */
+int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n);
/*
* ngtcp2_vec_split splits |src| to |dst| so that the sum of the
@@ -97,13 +103,13 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
/*
* ngtcp2_vec_copy_at_most copies |src| of length |srccnt| to |dst| of
* length |dstcnt|. The total number of bytes which the copied
- * ngtcp2_vec refers to is at most |left| and is assigned to
- * |*pnwritten|. The empty elements in |src| are ignored. This
- * function returns the number of elements copied.
+ * ngtcp2_vec refers to is at most |left|. The empty elements in
+ * |src| are ignored. This function returns the number of elements
+ * copied.
*/
-size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten,
- size_t dstcnt, const ngtcp2_vec *src,
- size_t srccnt, size_t left);
+size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt,
+ const ngtcp2_vec *src, size_t srccnt,
+ size_t left);
/*
* ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c
index 40f3ae3f9ea..b31162c3ebe 100644
--- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c
@@ -31,7 +31,7 @@
static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM,
NGTCP2_VERSION};
-ngtcp2_info *ngtcp2_version(int least_version) {
+const ngtcp2_info *ngtcp2_version(int least_version) {
if (least_version > NGTCP2_VERSION_NUM) {
return NULL;
}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c
new file mode 100644
index 00000000000..71c816e4d3d
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c
@@ -0,0 +1,99 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Translated to C from the original C++ code
+ * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h
+ * with the following license:
+ *
+ * // Copyright (c) 2016 The Chromium Authors. All rights reserved.
+ * // Use of this source code is governed by a BSD-style license that can be
+ * // found in the LICENSE file.
+ */
+#include "ngtcp2_window_filter.h"
+
+#include <string.h>
+
+void ngtcp2_window_filter_init(ngtcp2_window_filter *wf,
+ uint64_t window_length) {
+ wf->window_length = window_length;
+ memset(wf->estimates, 0, sizeof(wf->estimates));
+}
+
+void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time) {
+ if (wf->estimates[0].sample == 0 || new_sample > wf->estimates[0].sample ||
+ new_time - wf->estimates[2].time > wf->window_length) {
+ ngtcp2_window_filter_reset(wf, new_sample, new_time);
+ return;
+ }
+
+ if (new_sample > wf->estimates[1].sample) {
+ wf->estimates[1].sample = new_sample;
+ wf->estimates[1].time = new_time;
+ wf->estimates[2] = wf->estimates[1];
+ } else if (new_sample > wf->estimates[2].sample) {
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+ }
+
+ if (new_time - wf->estimates[0].time > wf->window_length) {
+ wf->estimates[0] = wf->estimates[1];
+ wf->estimates[1] = wf->estimates[2];
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+
+ if (new_time - wf->estimates[0].time > wf->window_length) {
+ wf->estimates[0] = wf->estimates[1];
+ wf->estimates[1] = wf->estimates[2];
+ }
+ return;
+ }
+
+ if (wf->estimates[1].sample == wf->estimates[0].sample &&
+ new_time - wf->estimates[1].time > wf->window_length >> 2) {
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+ wf->estimates[1] = wf->estimates[2];
+ return;
+ }
+
+ if (wf->estimates[2].sample == wf->estimates[1].sample &&
+ new_time - wf->estimates[2].time > wf->window_length >> 1) {
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+ }
+}
+
+void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time) {
+ wf->estimates[0].sample = new_sample;
+ wf->estimates[0].time = new_time;
+ wf->estimates[1] = wf->estimates[2] = wf->estimates[0];
+}
+
+uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf) {
+ return wf->estimates[0].sample;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h
new file mode 100644
index 00000000000..50415f10b8c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h
@@ -0,0 +1,65 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Translated to C from the original C++ code
+ * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h
+ * with the following license:
+ *
+ * // Copyright (c) 2016 The Chromium Authors. All rights reserved.
+ * // Use of this source code is governed by a BSD-style license that can be
+ * // found in the LICENSE file.
+ */
+#ifndef NGTCP2_WINDOW_FILTER_H
+#define NGTCP2_WINDOW_FILTER_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_window_filter_sample {
+ uint64_t sample;
+ uint64_t time;
+} ngtcp2_window_filter_sample;
+
+typedef struct ngtcp2_window_filter {
+ uint64_t window_length;
+ ngtcp2_window_filter_sample estimates[3];
+} ngtcp2_window_filter;
+
+void ngtcp2_window_filter_init(ngtcp2_window_filter *wf,
+ uint64_t window_length);
+
+void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time);
+
+void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time);
+
+uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf);
+
+#endif /* NGTCP2_WINDOW_FILTER_H */