summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE52
-rwxr-xr-xconfigure.py48
-rw-r--r--deps/ngtcp2/.gitignore8
-rw-r--r--deps/ngtcp2/LICENSE_nghttp322
-rw-r--r--deps/ngtcp2/LICENSE_ngtcp222
-rw-r--r--deps/ngtcp2/README.md45
-rw-r--r--deps/ngtcp2/config.h38
-rw-r--r--deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h2666
-rw-r--r--deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h46
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_buf.c90
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_buf.h74
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conn.c3523
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conn.h289
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conv.c134
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_conv.h221
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_debug.c61
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_debug.h44
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_err.c126
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_err.h34
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_frame.c218
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_frame.h243
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c130
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h104
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_http.c844
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_http.h146
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c88
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h99
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c749
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h331
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_macro.h47
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_map.c336
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_map.h154
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_mem.c77
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_mem.h45
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_pq.c168
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_pq.h129
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c4095
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h961
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c122
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h108
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c4981
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_range.c62
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_range.h81
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c109
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h82
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c159
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h113
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_str.c110
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_str.h40
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_stream.c1287
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_stream.h407
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c110
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h82
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_vec.c38
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_vec.h35
-rw-r--r--deps/ngtcp2/nghttp3/lib/nghttp3_version.c39
-rw-r--r--deps/ngtcp2/ngtcp2.gyp174
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c540
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h688
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h62
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h90
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c526
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/shared.c831
-rw-r--r--deps/ngtcp2/ngtcp2/crypto/shared.h211
-rw-r--r--deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h4831
-rw-r--r--deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h51
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c318
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h212
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c131
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h78
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c44
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h79
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c536
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h135
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c121
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h153
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c11236
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h822
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c257
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h285
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c749
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h103
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c144
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h34
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c129
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h103
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c86
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h95
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c741
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h329
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c767
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h80
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h53
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c332
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h152
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c75
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h43
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c74
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h49
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c2432
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h1195
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c230
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h153
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c164
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h126
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c180
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h193
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c1108
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h144
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c61
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h80
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h42
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c114
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h110
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c327
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h197
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c107
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h74
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c1301
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h402
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c242
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h106
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c675
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h268
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c232
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h114
-rw-r--r--deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c39
-rw-r--r--node.gypi20
-rw-r--r--src/node_metadata.cc10
-rw-r--r--src/node_metadata.h15
-rw-r--r--test/common/index.js3
-rw-r--r--test/parallel/test-process-versions.js19
-rw-r--r--tools/getsharedopensslhasquic.py19
-rwxr-xr-xtools/license-builder.sh2
134 files changed, 61505 insertions, 20 deletions
diff --git a/LICENSE b/LICENSE
index a62f3ad825..c6c4f14e2d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1578,3 +1578,55 @@ The externally maintained libraries used by Node.js are:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
+
+- ngtcp2, located at deps/ngtcp2/ngtcp2/, is licensed as follows:
+ """
+ The MIT License
+
+ Copyright (c) 2016 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.
+ """
+
+- nghttp3, located at deps/ngtcp2/nghttp3/, is licensed as follows:
+ """
+ The MIT License
+
+ Copyright (c) 2019 nghttp3 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.
+ """
diff --git a/configure.py b/configure.py
index bb0f8019da..b526507fee 100755
--- a/configure.py
+++ b/configure.py
@@ -284,6 +284,50 @@ shared_optgroup.add_argument('--shared-nghttp2-libpath',
dest='shared_nghttp2_libpath',
help='a directory to search for the shared nghttp2 DLLs')
+shared_optgroup.add_argument('--shared-nghttp3',
+ action='store_true',
+ dest='shared_nghttp3',
+ default=None,
+ help='link to a shared nghttp3 DLL instead of static linking')
+
+shared_optgroup.add_argument('--shared-nghttp3-includes',
+ action='store',
+ dest='shared_nghttp3_includes',
+ help='directory containing nghttp3 header files')
+
+shared_optgroup.add_argument('--shared-nghttp3-libname',
+ action='store',
+ dest='shared_nghttp3_libname',
+ default='nghttp3',
+ help='alternative lib name to link to [default: %(default)s]')
+
+shared_optgroup.add_argument('--shared-nghttp3-libpath',
+ action='store',
+ dest='shared_nghttp3_libpath',
+ help='a directory to search for the shared nghttp3 DLLs')
+
+shared_optgroup.add_argument('--shared-ngtcp2',
+ action='store_true',
+ dest='shared_ngtcp2',
+ default=None,
+ help='link to a shared ngtcp2 DLL instead of static linking')
+
+shared_optgroup.add_argument('--shared-ngtcp2-includes',
+ action='store',
+ dest='shared_ngtcp2_includes',
+ help='directory containing ngtcp2 header files')
+
+shared_optgroup.add_argument('--shared-ngtcp2-libname',
+ action='store',
+ dest='shared_ngtcp2_libname',
+ default='ngtcp2',
+ help='alternative lib name to link to [default: %(default)s]')
+
+shared_optgroup.add_argument('--shared-ngtcp2-libpath',
+ action='store',
+ dest='shared_ngtcp2_libpath',
+ help='a directory to search for the shared tcp2 DLLs')
+
shared_optgroup.add_argument('--shared-openssl',
action='store_true',
dest='shared_openssl',
@@ -1347,6 +1391,8 @@ def configure_openssl(o):
variables = o['variables']
variables['node_use_openssl'] = b(not options.without_ssl)
variables['node_shared_openssl'] = b(options.shared_openssl)
+ variables['node_shared_ngtcp2'] = b(options.shared_ngtcp2)
+ variables['node_shared_nghttp3'] = b(options.shared_nghttp3)
variables['openssl_is_fips'] = b(options.openssl_is_fips)
variables['openssl_fips'] = ''
variables['openssl_quic'] = b(True)
@@ -1836,6 +1882,8 @@ configure_library('libuv', output)
configure_library('brotli', output, pkgname=['libbrotlidec', 'libbrotlienc'])
configure_library('cares', output, pkgname='libcares')
configure_library('nghttp2', output, pkgname='libnghttp2')
+configure_library('nghttp3', output, pkgname='libnghttp3')
+configure_library('ngtcp2', output, pkgname='libngtcp2')
configure_v8(output)
configure_openssl(output)
configure_intl(output)
diff --git a/deps/ngtcp2/.gitignore b/deps/ngtcp2/.gitignore
new file mode 100644
index 0000000000..40909a37dd
--- /dev/null
+++ b/deps/ngtcp2/.gitignore
@@ -0,0 +1,8 @@
+*.in
+*.am
+*.txt
+*.pc
+Makefile
+*gnutls*
+ngtcp2/**/.gitignore
+ngtcp2/**/.deps
diff --git a/deps/ngtcp2/LICENSE_nghttp3 b/deps/ngtcp2/LICENSE_nghttp3
new file mode 100644
index 0000000000..37562ea58c
--- /dev/null
+++ b/deps/ngtcp2/LICENSE_nghttp3
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2019 nghttp3 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.
diff --git a/deps/ngtcp2/LICENSE_ngtcp2 b/deps/ngtcp2/LICENSE_ngtcp2
new file mode 100644
index 0000000000..9b367cdce7
--- /dev/null
+++ b/deps/ngtcp2/LICENSE_ngtcp2
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2016 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.
diff --git a/deps/ngtcp2/README.md b/deps/ngtcp2/README.md
new file mode 100644
index 0000000000..57043dcc13
--- /dev/null
+++ b/deps/ngtcp2/README.md
@@ -0,0 +1,45 @@
+# ngtcp2 and nghttp3
+
+The ngtcp2 and nghttp3 dependencies provide the core functionality for
+QUIC and HTTP/3.
+
+The sources are pulled from:
+
+* ngtcp2: https://github.com/ngtcp2/ngtcp2
+* nghttp3: https://github.com/ngtcp2/nghttp3
+
+In both the `ngtcp2` and `nghttp3` git repos, the active development occurs
+in the default branch (currently named `master` in each).
+
+We only use a subset of the sources for each.
+
+## Updating
+
+The `nghttp3` library depends on `ngtcp2`. Both should always be updated
+together. From `ngtcp2` we only want the contents of the `lib` and `crypto`
+directories; from `nghttp3` we only want the contents o the `lib`.
+
+### Updating ngtcp2
+
+To update ngtcp2:
+
+```sh
+$ git clone https://github.com/ngtcp2/ngtcp2
+$ cd ngtcp2
+$ autoreconf -i
+$ ./configure --prefix=$PWD/build --enable-lib-only
+$ cp -R lib/* ../node/deps/ngtcp2/ngtcp2/lib/
+$ cp -R crypto/* ../node/deps/ngtcp2/ngtcp2/crypto/
+```
+
+### Updating nghttp3
+
+To update ngtcp2:
+
+```sh
+$ git clone https://github.com/ngtcp2/nghttp3
+$ cd nghttp3
+$ autoreconf -i
+$ ./configure --prefix=$PWD/build --enable-lib-only
+$ cp -R lib/* ../node/deps/ngtcp2/nghttp3/lib/
+```
diff --git a/deps/ngtcp2/config.h b/deps/ngtcp2/config.h
new file mode 100644
index 0000000000..f57b84208c
--- /dev/null
+++ b/deps/ngtcp2/config.h
@@ -0,0 +1,38 @@
+/* Edited to match src/node.h. */
+#include <stdint.h>
+
+#ifdef _WIN32
+#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
+typedef intptr_t ssize_t;
+# define _SSIZE_T_
+# define _SSIZE_T_DEFINED
+#endif
+#else // !_WIN32
+# include <sys/types.h> // size_t, ssize_t
+#endif // _WIN32
+
+#ifdef _MSC_VER
+# include <intrin.h>
+# define __builtin_popcount __popcnt
+#endif
+
+/* Define to 1 to enable debug output. */
+/* #undef DEBUGBUILD */
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+/* #undef HAVE_ARPA_INET_H */
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h
new file mode 100644
index 0000000000..bc66c40ce2
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h
@@ -0,0 +1,2666 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2018 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2017 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_H
+#define NGHTTP3_H
+
+/* Define WIN32 when build target is Win32 API (borrowed from
+ libcurl) */
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+/* MSVC < 2013 does not have inttypes.h because it is not C99
+ compliant. See compiler macros and version number in
+ https://sourceforge.net/p/predef/wiki/Compilers/ */
+# include <stdint.h>
+#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+# include <inttypes.h>
+#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <nghttp3/version.h>
+
+#ifdef NGHTTP3_STATICLIB
+# define NGHTTP3_EXTERN
+#elif defined(WIN32)
+# ifdef BUILDING_NGHTTP3
+# define NGHTTP3_EXTERN __declspec(dllexport)
+# else /* !BUILDING_NGHTTP3 */
+# define NGHTTP3_EXTERN __declspec(dllimport)
+# endif /* !BUILDING_NGHTTP3 */
+#else /* !defined(WIN32) */
+# ifdef BUILDING_NGHTTP3
+# define NGHTTP3_EXTERN __attribute__((visibility("default")))
+# else /* !BUILDING_NGHTTP3 */
+# define NGHTTP3_EXTERN
+# endif /* !BUILDING_NGHTTP3 */
+#endif /* !defined(WIN32) */
+
+/**
+ * @typedef
+ *
+ * :type:`nghttp3_ssize` is signed counterpart of size_t.
+ */
+typedef ptrdiff_t nghttp3_ssize;
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ALPN_H3` is a serialized form of HTTP/3 ALPN
+ * protocol identifier this library supports. Notice that the first
+ * byte is the length of the following protocol identifier.
+ */
+#define NGHTTP3_ALPN_H3 "\x2h3"
+
+/**
+ * @macrosection
+ *
+ * nghttp3 library error codes
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` indicates that a passed
+ * argument is invalid.
+ */
+#define NGHTTP3_ERR_INVALID_ARGUMENT -101
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_NOBUF` indicates that a provided buffer does
+ * not have enough space to store data.
+ */
+#define NGHTTP3_ERR_NOBUF -102
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE` indicates that a requested
+ * operation is not allowed at the current connection state.
+ */
+#define NGHTTP3_ERR_INVALID_STATE -103
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK` indicates that an operation might
+ * block.
+ */
+#define NGHTTP3_ERR_WOULDBLOCK -104
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_IN_USE` indicates that a stream ID is
+ * already in use.
+ */
+#define NGHTTP3_ERR_STREAM_IN_USE -105
+/**
+ * @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.
+ */
+#define NGHTTP3_ERR_MALFORMED_HTTP_HEADER -107
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_REMOVE_HTTP_HEADER` indicates that an HTTP
+ * header field is discarded.
+ */
+#define NGHTTP3_ERR_REMOVE_HTTP_HEADER -108
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING` indicates that HTTP
+ * messaging is malformed.
+ */
+#define NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING -109
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL` indicates that a fatal error is
+ * occurred during QPACK processing and it cannot be recoverable.
+ */
+#define NGHTTP3_ERR_QPACK_FATAL -111
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` indicates that a header
+ * field is too large to process.
+ */
+#define NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE -112
+/**
+ * @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.
+ */
+#define NGHTTP3_ERR_STREAM_NOT_FOUND -114
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_IGNORE_PUSH_PROMISE` indicates that a push
+ * promise should be ignored.
+ */
+#define NGHTTP3_ERR_IGNORE_PUSH_PROMISE -115
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is
+ * closing state.
+ */
+#define NGHTTP3_ERR_CONN_CLOSING -116
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` indicates that a
+ * QPACK decompression failed.
+ */
+#define NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED -402
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR` indicates that an
+ * error occurred while reading QPACK encoder stream.
+ */
+#define NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR -403
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` indicates that an
+ * error occurred while reading QPACK decoder stream.
+ */
+#define NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR -404
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_FRAME_UNEXPECTED` indicates that an
+ * unexpected HTTP/3 frame is received.
+ */
+#define NGHTTP3_ERR_H3_FRAME_UNEXPECTED -408
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_FRAME_ERROR` indicates that an HTTP/3 frame
+ * is malformed.
+ */
+#define NGHTTP3_ERR_H3_FRAME_ERROR -409
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_MISSING_SETTINGS` indicates that an HTTP/3
+ * SETTINGS frame is missing.
+ */
+#define NGHTTP3_ERR_H3_MISSING_SETTINGS -665
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_INTERNAL_ERROR` indicates an internal error.
+ */
+#define NGHTTP3_ERR_H3_INTERNAL_ERROR -667
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` indicates that a
+ * critical stream is closed.
+ */
+#define NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM -668
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR` indicates a general
+ * protocol error. This is typically a catch-all error.
+ */
+#define NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR -669
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_ID_ERROR` indicates that an ID related error
+ * occurred.
+ */
+#define NGHTTP3_ERR_H3_ID_ERROR -670
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_SETTINGS_ERROR` indicates that an HTTP/3
+ * SETTINGS frame is malformed.
+ */
+#define NGHTTP3_ERR_H3_SETTINGS_ERROR -671
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_STREAM_CREATION_ERROR` indicates that a
+ * remote endpoint attempts to create a new stream which is not
+ * allowed.
+ */
+#define NGHTTP3_ERR_H3_STREAM_CREATION_ERROR -672
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_FATAL` indicates that error codes less than
+ * this value is fatal error. When this error is returned, an
+ * endpoint should drop connection immediately.
+ */
+#define NGHTTP3_ERR_FATAL -900
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM` indicates out of memory.
+ */
+#define NGHTTP3_ERR_NOMEM -901
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` indicates that user defined
+ * callback function failed.
+ */
+#define NGHTTP3_ERR_CALLBACK_FAILURE -902
+
+/**
+ * @macrosection
+ *
+ * HTTP/3 application error code
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_NO_ERROR` is HTTP/3 application error code
+ * ``H3_NO_ERROR``.
+ */
+#define NGHTTP3_H3_NO_ERROR 0x0100
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_GENERAL_PROTOCOL_ERROR` is HTTP/3 application
+ * error code ``H3_GENERAL_PROTOCOL_ERROR``.
+ */
+#define NGHTTP3_H3_GENERAL_PROTOCOL_ERROR 0x0101
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_INTERNAL_ERROR` is HTTP/3 application error code
+ * ``H3_INTERNAL_ERROR``.
+ */
+#define NGHTTP3_H3_INTERNAL_ERROR 0x0102
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_STREAM_CREATION_ERROR` is HTTP/3 application
+ * error code ``H3_STREAM_CREATION_ERROR``.
+ */
+#define NGHTTP3_H3_STREAM_CREATION_ERROR 0x0103
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_CLOSED_CRITICAL_STREAM` is HTTP/3 application
+ * error code ``H3_CLOSED_CRITICAL_STREAM``.
+ */
+#define NGHTTP3_H3_CLOSED_CRITICAL_STREAM 0x0104
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_FRAME_UNEXPECTED` is HTTP/3 application error
+ * code ``H3_FRAME_UNEXPECTED``.
+ */
+#define NGHTTP3_H3_FRAME_UNEXPECTED 0x0105
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_FRAME_ERROR` is HTTP/3 application error code
+ * ``H3_FRAME_ERROR``.
+ */
+#define NGHTTP3_H3_FRAME_ERROR 0x0106
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_EXCESSIVE_LOAD` is HTTP/3 application error code
+ * ``H3_EXCESSIVE_LOAD``.
+ */
+#define NGHTTP3_H3_EXCESSIVE_LOAD 0x0107
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_ID_ERROR` is HTTP/3 application error code
+ * ``H3_ID_ERROR``.
+ */
+#define NGHTTP3_H3_ID_ERROR 0x0108
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_SETTINGS_ERROR` is HTTP/3 application error code
+ * ``H3_SETTINGS_ERROR``.
+ */
+#define NGHTTP3_H3_SETTINGS_ERROR 0x0109
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_MISSING_SETTINGS` is HTTP/3 application error
+ * code ``H3_MISSING_SETTINGS``.
+ */
+#define NGHTTP3_H3_MISSING_SETTINGS 0x010a
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_REJECTED` is HTTP/3 application error
+ * code ``H3_REQUEST_REJECTED``.
+ */
+#define NGHTTP3_H3_REQUEST_REJECTED 0x010b
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_CANCELLED` is HTTP/3 application error
+ * code ``H3_REQUEST_CANCELLED``.
+ */
+#define NGHTTP3_H3_REQUEST_CANCELLED 0x010c
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_INCOMPLETE` is HTTP/3 application error
+ * code ``H3_REQUEST_INCOMPLETE``.
+ */
+#define NGHTTP3_H3_REQUEST_INCOMPLETE 0x010d
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_MESSAGE_ERROR` is HTTP/3 application error code
+ * ``H3_MESSAGE_ERROR``.
+ */
+#define NGHTTP3_H3_MESSAGE_ERROR 0x010e
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_CONNECT_ERROR` is HTTP/3 application error code
+ * ``H3_CONNECT_ERROR``.
+ */
+#define NGHTTP3_H3_CONNECT_ERROR 0x010f
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_VERSION_FALLBACK` is HTTP/3 application error
+ * code ``H3_VERSION_FALLBACK``.
+ */
+#define NGHTTP3_H3_VERSION_FALLBACK 0x0110
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECOMPRESSION_FAILED` is HTTP/3 application
+ * error code ``QPACK_DECOMPRESSION_FAILED``.
+ */
+#define NGHTTP3_QPACK_DECOMPRESSION_FAILED 0x0200
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_ENCODER_STREAM_ERROR` is HTTP/3 application
+ * error code ``QPACK_ENCODER_STREAM_ERROR``.
+ */
+#define NGHTTP3_QPACK_ENCODER_STREAM_ERROR 0x0201
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODER_STREAM_ERROR` is HTTP/3 application
+ * error code ``QPACK_DECODER_STREAM_ERROR``.
+ */
+#define NGHTTP3_QPACK_DECODER_STREAM_ERROR 0x0202
+
+/**
+ * @functypedef
+ *
+ * Custom memory allocator to replace malloc(). The |mem_user_data|
+ * is the mem_user_data member of :type:`nghttp3_mem` structure.
+ */
+typedef void *(*nghttp3_malloc)(size_t size, void *mem_user_data);
+
+/**
+ * @functypedef
+ *
+ * Custom memory allocator to replace free(). The |mem_user_data| is
+ * the mem_user_data member of :type:`nghttp3_mem` structure.
+ */
+typedef void (*nghttp3_free)(void *ptr, void *mem_user_data);
+
+/**
+ * @functypedef
+ *
+ * Custom memory allocator to replace calloc(). The |mem_user_data|
+ * is the mem_user_data member of :type:`nghttp3_mem` structure.
+ */
+typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *mem_user_data);
+
+/**
+ * @functypedef
+ *
+ * Custom memory allocator to replace realloc(). The |mem_user_data|
+ * is the mem_user_data member of :type:`nghttp3_mem` structure.
+ */
+typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_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
+ * 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::
+ *
+ * void *my_malloc_cb(size_t size, void *mem_user_data) {
+ * return my_malloc(size);
+ * }
+ *
+ * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }
+ *
+ * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {
+ * return my_calloc(nmemb, size);
+ * }
+ *
+ * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {
+ * return my_realloc(ptr, size);
+ * }
+ *
+ * void conn_new() {
+ * nghttp3_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,
+ * my_realloc_cb};
+ *
+ * ...
+ * }
+ */
+typedef struct nghttp3_mem {
+ /**
+ * :member:`mem_user_data` is an arbitrary user supplied data. This
+ * is passed to each allocator function.
+ */
+ void *mem_user_data;
+ /**
+ * :member:`malloc` is a custom allocator function to replace
+ * malloc().
+ */
+ nghttp3_malloc malloc;
+ /**
+ * :member:`free` is a custom allocator function to replace free().
+ */
+ nghttp3_free free;
+ /**
+ * :member:`calloc` is a custom allocator function to replace
+ * calloc().
+ */
+ nghttp3_calloc calloc;
+ /**
+ * :member:`realloc` is a custom allocator function to replace
+ * realloc().
+ */
+ nghttp3_realloc realloc;
+} nghttp3_mem;
+
+/**
+ * @function
+ *
+ * `nghttp3_mem_default` returns the default memory allocator which
+ * uses malloc/calloc/realloc/free.
+ */
+NGHTTP3_EXTERN const nghttp3_mem *nghttp3_mem_default(void);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_vec` is ``struct iovec`` compatible structure to
+ * reference arbitrary array of bytes.
+ */
+typedef struct nghttp3_vec {
+ /**
+ * :member:`base` points to the data.
+ */
+ uint8_t *base;
+ /**
+ * :member:`len` is the number of bytes which the buffer pointed by
+ * base contains.
+ */
+ size_t len;
+} nghttp3_vec;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_rcbuf` is the object representing reference counted
+ * buffer. The details of this structure are intentionally hidden
+ * from the public API.
+ */
+typedef struct nghttp3_rcbuf nghttp3_rcbuf;
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_incref` increments the reference count of |rcbuf| by
+ * 1.
+ */
+NGHTTP3_EXTERN void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_decref` decrements the reference count of |rcbuf| by
+ * 1. If the reference count becomes zero, the object pointed by
+ * |rcbuf| will be freed. In this case, application must not use
+ * |rcbuf| again.
+ */
+NGHTTP3_EXTERN void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_get_buf` returns the underlying buffer managed by
+ * |rcbuf|.
+ */
+NGHTTP3_EXTERN nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_is_static` returns nonzero if the underlying buffer
+ * is statically allocated, and 0 otherwise. This can be useful for
+ * language bindings that wish to avoid creating duplicate strings for
+ * these buffers.
+ */
+NGHTTP3_EXTERN int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_buf` is the variable size buffer.
+ */
+typedef struct nghttp3_buf {
+ /**
+ * :member:`begin` points to the beginning of the buffer.
+ */
+ uint8_t *begin;
+ /**
+ * :member:`end` points to the one beyond of the last byte of the
+ * buffer
+ */
+ uint8_t *end;
+ /**
+ * :member:`pos` pointers to the start of data. Typically, this
+ * points to the point that next data should be read. Initially, it
+ * points to :member:`begin`.
+ */
+ uint8_t *pos;
+ /**
+ * :member:`last` points to the one beyond of the last data of the
+ * buffer. Typically, new data is written at this point.
+ * Initially, it points to :member:`begin`.
+ */
+ uint8_t *last;
+} nghttp3_buf;
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_init` initializes empty |buf|.
+ */
+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|.
+ */
+NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `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.
+ */
+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.
+ */
+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_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
+
+/**
+ * @macrosection
+ *
+ * Flags for header field name/value pair
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set.
+ */
+#define NGHTTP3_NV_FLAG_NONE 0
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NEVER_INDEX` indicates that this name/value
+ * pair must not be indexed. Other implementation calls this bit as
+ * "sensitive".
+ */
+#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NO_COPY_NAME` is set solely by application.
+ * 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
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NO_COPY_VALUE` is set solely by
+ * 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
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_nv` is the name/value pair, which mainly used to
+ * represent header fields.
+ */
+typedef struct nghttp3_nv {
+ /**
+ * :member:`name` is the header field name.
+ */
+ uint8_t *name;
+ /**
+ * :member:`value` is the header field value.
+ */
+ uint8_t *value;
+ /**
+ * :member:`namelen` is the length of the |name|, excluding
+ * terminating NULL.
+ */
+ size_t namelen;
+ /**
+ * :member:`valuelen` is the length of the |value|, excluding
+ * terminating NULL.
+ */
+ size_t valuelen;
+ /**
+ * :member:`flags` is bitwise OR of one or more of
+ * NGHTTP3_NV_FLAG_*.
+ */
+ uint8_t flags;
+} nghttp3_nv;
+
+/* Generated by mkstatichdtbl.py */
+/**
+ * @enum
+ *
+ * :type:`nghttp3_qpack_token` defines HTTP header field name tokens
+ * to identify field name quickly. It appears in
+ * :member:`nghttp3_qpack_nv.token`.
+ */
+typedef enum nghttp3_qpack_token {
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__AUTHORITY` is a token for
+ * ``:authority``.
+ */
+ NGHTTP3_QPACK_TOKEN__AUTHORITY = 0,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__PATH` is a token for ``:path``.
+ */
+ NGHTTP3_QPACK_TOKEN__PATH = 8,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_AGE` is a token for ``age``.
+ */
+ NGHTTP3_QPACK_TOKEN_AGE = 43,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION` is a token for
+ * ``content-disposition``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION = 52,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH` is a token for
+ * ``content-length``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH = 55,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_COOKIE` is a token for ``cookie``.
+ */
+ NGHTTP3_QPACK_TOKEN_COOKIE = 68,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_DATE` is a token for ``date``.
+ */
+ NGHTTP3_QPACK_TOKEN_DATE = 69,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ETAG` is a token for ``etag``.
+ */
+ NGHTTP3_QPACK_TOKEN_ETAG = 71,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE` is a token for
+ * ``if-modified-since``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE = 74,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH` is a token for
+ * ``if-none-match``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH = 75,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LAST_MODIFIED` is a token for
+ * ``last-modified``.
+ */
+ NGHTTP3_QPACK_TOKEN_LAST_MODIFIED = 77,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LINK` is a token for ``link``.
+ */
+ NGHTTP3_QPACK_TOKEN_LINK = 78,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LOCATION` is a token for ``location``.
+ */
+ NGHTTP3_QPACK_TOKEN_LOCATION = 79,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_REFERER` is a token for ``referer``.
+ */
+ NGHTTP3_QPACK_TOKEN_REFERER = 83,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_SET_COOKIE` is a token for
+ * ``set-cookie``.
+ */
+ NGHTTP3_QPACK_TOKEN_SET_COOKIE = 85,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__METHOD` is a token for ``:method``.
+ */
+ NGHTTP3_QPACK_TOKEN__METHOD = 1,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__SCHEME` is a token for ``:scheme``.
+ */
+ NGHTTP3_QPACK_TOKEN__SCHEME = 9,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__STATUS` is a token for ``:status``.
+ */
+ NGHTTP3_QPACK_TOKEN__STATUS = 11,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT` is a token for ``accept``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT = 25,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING` is a token for
+ * ``accept-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING = 27,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES` is a token for
+ * ``accept-ranges``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES = 29,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS` is a
+ * token for ``access-control-allow-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS = 32,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN` is a
+ * token for ``access-control-allow-origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 38,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CACHE_CONTROL` is a token for
+ * ``cache-control``.
+ */
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL = 46,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING` is a token for
+ * ``content-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING = 53,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_TYPE` is a token for
+ * ``content-type``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE = 57,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_RANGE` is a token for ``range``.
+ */
+ NGHTTP3_QPACK_TOKEN_RANGE = 82,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY` is a token
+ * for ``strict-transport-security``.
+ */
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY = 86,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_VARY` is a token for ``vary``.
+ */
+ NGHTTP3_QPACK_TOKEN_VARY = 92,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS` is a token for
+ * ``x-content-type-options``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS = 94,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION` is a token for
+ * ``x-xss-protection``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION = 98,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE` is a token for
+ * ``accept-language``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE = 28,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS` is a
+ * token for ``access-control-allow-credentials``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS = 30,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS` is a
+ * token for ``access-control-allow-methods``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS = 35,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS` is a
+ * token for ``access-control-expose-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS = 39,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS` is a
+ * token for ``access-control-request-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS = 40,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD` is a
+ * token for ``access-control-request-method``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD = 41,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ALT_SVC` is a token for ``alt-svc``.
+ */
+ NGHTTP3_QPACK_TOKEN_ALT_SVC = 44,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_AUTHORIZATION` is a token for
+ * ``authorization``.
+ */
+ NGHTTP3_QPACK_TOKEN_AUTHORIZATION = 45,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY` is a token
+ * for ``content-security-policy``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY = 56,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_EARLY_DATA` is a token for
+ * ``early-data``.
+ */
+ NGHTTP3_QPACK_TOKEN_EARLY_DATA = 70,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_EXPECT_CT` is a token for
+ * ``expect-ct``.
+ */
+ NGHTTP3_QPACK_TOKEN_EXPECT_CT = 72,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_FORWARDED` is a token for
+ * ``forwarded``.
+ */
+ NGHTTP3_QPACK_TOKEN_FORWARDED = 73,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_RANGE` is a token for ``if-range``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_RANGE = 76,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ORIGIN` is a token for ``origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_ORIGIN = 80,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PURPOSE` is a token for ``purpose``.
+ */
+ NGHTTP3_QPACK_TOKEN_PURPOSE = 81,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_SERVER` is a token for ``server``.
+ */
+ NGHTTP3_QPACK_TOKEN_SERVER = 84,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN` is a token for
+ * ``timing-allow-origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN = 89,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS` is a token
+ * for ``upgrade-insecure-requests``.
+ */
+ NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS = 90,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_USER_AGENT` is a token for
+ * ``user-agent``.
+ */
+ NGHTTP3_QPACK_TOKEN_USER_AGENT = 91,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR` is a token for
+ * ``x-forwarded-for``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR = 95,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS` is a token for
+ * ``x-frame-options``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS = 96,
+
+ /* Additional header fields for HTTP messaging validation */
+
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_HOST` is a token for ``host``.
+ */
+ NGHTTP3_QPACK_TOKEN_HOST = 1000,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONNECTION` is a token for
+ * ``connection``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONNECTION,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_KEEP_ALIVE` is a token for
+ * ``keep-alive``.
+ */
+ NGHTTP3_QPACK_TOKEN_KEEP_ALIVE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION` is a token for
+ * ``proxy-connection``.
+ */
+ NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING` is a token for
+ * ``transfer-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE` is a token for ``upgrade``.
+ */
+ NGHTTP3_QPACK_TOKEN_UPGRADE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TE` is a token for ``te``.
+ */
+ NGHTTP3_QPACK_TOKEN_TE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__PROTOCOL` is a token for
+ * ``:protocol``.
+ */
+ NGHTTP3_QPACK_TOKEN__PROTOCOL,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PRIORITY` is a token for ``priority``.
+ */
+ NGHTTP3_QPACK_TOKEN_PRIORITY
+} nghttp3_qpack_token;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_nv` represents header field name/value pair
+ * just like :type:`nghttp3_nv`. It is an extended version of
+ * :type:`nghttp3_nv` and has reference counted buffers and tokens
+ * which might be useful for applications.
+ */
+typedef struct {
+ /**
+ * :member:`name` is the buffer containing header field name.
+ * NULL-termination is guaranteed.
+ */
+ nghttp3_rcbuf *name;
+ /**
+ * :member:`value` is the buffer containing header field value.
+ * NULL-termination is guaranteed.
+ */
+ nghttp3_rcbuf *value;
+ /**
+ * :member:`token` is :type:`nghttp3_qpack_token` value of
+ * :member:`name`. It could be -1 if we have no token for that
+ * header field name.
+ */
+ int32_t token;
+ /**
+ * :member:`flags` is a bitwise OR of one or more of
+ * NGHTTP3_NV_FLAG_*. See :macro:`NGHTTP3_NV_FLAG_NONE`.
+ */
+ uint8_t flags;
+} nghttp3_qpack_nv;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_encoder` is QPACK encoder.
+ */
+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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
+ size_t max_dtable_size,
+ size_t max_blocked,
+ const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_del` frees memory allocated for |encoder|.
+ * This function frees memory pointed by |encoder| itself.
+ */
+NGHTTP3_EXTERN void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_encode` encodes the list of header fields
+ * |nva|. |nvlen| is the length of |nva|. |stream_id| is the
+ * identifier of the stream which this header fields belong to. This
+ * function writes header block prefix, encoded header fields, and
+ * encoder stream to |pbuf|, |rbuf|, and |ebuf| respectively. The
+ * :member:`nghttp3_buf.last` will be adjusted when data is written.
+ * An application should write |pbuf| and |rbuf| to the request stream
+ * in this order.
+ *
+ * The buffer pointed by |pbuf|, |rbuf|, and |ebuf| can be empty
+ * buffer. It is fine to pass a buffer initialized by
+ * `nghttp3_buf_init(buf) <nghttp3_buf_init>`. This function
+ * allocates memory for these buffers as necessary. In particular, it
+ * frees and expands buffer if the current capacity of buffer is not
+ * enough. If :member:`nghttp3_buf.begin` of any buffer is not NULL,
+ * it must be allocated by the same memory allocator passed to
+ * `nghttp3_qpack_encoder_new()`.
+ *
+ * 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_FATAL`
+ * |encoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_encoder_encode(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, nghttp3_buf *rbuf,
+ nghttp3_buf *ebuf, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_read_decoder` reads decoder stream. The
+ * buffer pointed by |src| of length |srclen| contains decoder stream.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |encoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM`
+ * |encoder| is unable to process input because it is malformed.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder(
+ nghttp3_qpack_encoder *encoder, const uint8_t *src, size_t srclen);
+
+/**
+ * @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_EXTERN void
+nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+/**
+ * @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_EXTERN int
+nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder,
+ uint64_t n);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_ack_everything` tells |encoder| that all
+ * encoded header blocks are acknowledged. 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_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_EXTERN size_t
+nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_stream_context` is a decoder context for an
+ * individual stream. Its state is per header block. In order to
+ * reuse this object for another header block, call
+ * `nghttp3_qpack_stream_context_reset`.
+ */
+typedef struct nghttp3_qpack_stream_context nghttp3_qpack_stream_context;
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_new` initializes stream context.
+ * |psctx| must be non-NULL pointer. |stream_id| is stream ID. |mem|
+ * is a memory allocator. This function allocates memory for
+ * :type:`nghttp3_qpack_stream_context` itself and assigns its pointer
+ * to |*psctx| if it succeeds.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx,
+ int64_t stream_id, const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_del` frees memory allocated for
+ * |sctx|. This function frees memory pointed by |sctx| itself.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_get_ricnt` returns required insert
+ * count.
+ */
+NGHTTP3_EXTERN uint64_t
+nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_reset` resets the state of |sctx|.
+ * Then it can be reused for an another header block in the same
+ * stream.
+ */
+NGHTTP3_EXTERN
+void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_decoder` is QPACK decoder.
+ */
+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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
+ size_t max_dtable_size,
+ size_t max_blocked,
+ const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_del` frees memory allocated for |decoder|.
+ * This function frees memory pointed by |decoder| itself.
+ */
+NGHTTP3_EXTERN void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_read_encoder` reads encoder stream. The
+ * buffer pointed by |src| of length |srclen| contains encoder stream.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |decoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM`
+ * Could not interpret encoder stream instruction.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_encoder(
+ nghttp3_qpack_decoder *decoder, const uint8_t *src, size_t srclen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_get_icnt` returns insert count.
+ */
+NGHTTP3_EXTERN uint64_t
+nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
+
+/**
+ * @macrosection
+ *
+ * Flags for QPACK decoder
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header
+ * field is successfully decoded.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header
+ * fields have been decoded.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding
+ * has been blocked.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_read_request` reads request stream. The
+ * request stream is given as the buffer pointed by |src| of length
+ * |srclen|. |sctx| is the stream context and it must be created by
+ * `nghttp3_qpack_stream_context_new()`. |*pflags| must be non-NULL
+ * pointer. |nv| must be non-NULL pointer.
+ *
+ * If this function succeeds, it assigns flags to |*pflags|. If
+ * |*pflags| has :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a
+ * decoded header field is assigned to |nv|. If |*pflags| has
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, all header fields
+ * have been successfully decoded. If |*pflags| has
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked
+ * 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
+ * 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.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |decoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED`
+ * Could not interpret header block instruction.
+ * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE`
+ * Header field is too large.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_request(
+ nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv, uint8_t *pflags, const uint8_t *src, size_t srclen,
+ int fin);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_write_decoder` writes decoder stream into
+ * |dbuf|.
+ *
+ * The caller must ensure that `nghttp3_buf_left(dbuf)
+ * <nghttp3_buf_left>` >=
+ * `nghttp3_qpack_decoder_get_decoder_streamlen(decoder)
+ * <nghttp3_qpack_decoder_get_decoder_streamlen>`.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
+ nghttp3_buf *dbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_get_decoder_streamlen` returns the length of
+ * decoder stream.
+ */
+NGHTTP3_EXTERN size_t
+nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_cancel_stream` cancels header decoding for
+ * stream denoted by |stream_id|.
+ *
+ * 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_FATAL`
+ * Decoder stream overflow.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
+ int64_t stream_id);
+
+/**
+ * @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_EXTERN void
+nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder,
+ size_t cap);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_set_max_concurrent_streams` tells |decoder|
+ * the maximum number of concurrent streams that a remote endpoint can
+ * open, including both bidirectional and unidirectional streams which
+ * potentially receive QPACK encoded HEADERS frame. This value is
+ * used as a hint to limit the length of decoder stream.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_decoder_set_max_concurrent_streams(nghttp3_qpack_decoder *decoder,
+ size_t max_concurrent_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_strerror` returns textual representation of |liberr|.
+ */
+NGHTTP3_EXTERN const char *nghttp3_strerror(int liberr);
+
+/**
+ * @function
+ *
+ * `nghttp3_err_infer_quic_app_error_code` returns a QUIC application
+ * error code which corresponds to |liberr|.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr);
+
+/**
+ * @functypedef
+ *
+ * :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)``
+ *
+ * The debug output is only enabled if the library is built with
+ * ``DEBUGBUILD`` macro defined.
+ */
+typedef void (*nghttp3_debug_vprintf_callback)(const char *format,
+ va_list args);
+
+/**
+ * @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.
+ *
+ * For builds without ``DEBUGBUILD`` macro defined, this function is
+ * noop.
+ *
+ * Note that building with ``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.
+ */
+NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_conn` represents a single HTTP/3 connection.
+ */
+typedef struct nghttp3_conn nghttp3_conn;
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_acked_stream_data` is a callback function which is
+ * invoked when data sent on stream denoted by |stream_id| supplied
+ * from application is acknowledged by remote endpoint. The number of
+ * bytes acknowledged is given in |datalen|.
+ *
+ * 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_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id,
+ size_t datalen, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_conn_stream_close` is a callback function which is
+ * invoked when a stream identified by |stream_id| is closed.
+ * |app_error_code| indicates the reason of this closure.
+ *
+ * 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_stream_close)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_recv_data` is a callback function which is invoked
+ * when a part of request or response body on stream identified by
+ * |stream_id| is received. |data| points to the received data and
+ * its length is |datalen|.
+ *
+ * The application is responsible for increasing flow control credit
+ * by |datalen| bytes.
+ *
+ * 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_data)(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *data, size_t datalen,
+ void *conn_user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_deferred_consume` is a callback function which is
+ * invoked when the library consumed |consumed| bytes for a stream
+ * identified by |stream_id|. This callback is used to notify the
+ * consumed bytes for stream blocked by QPACK decoder. The
+ * application is responsible for increasing flow control credit by
+ * |consumed| bytes.
+ *
+ * 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_deferred_consume)(nghttp3_conn *conn, int64_t stream_id,
+ size_t consumed, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_begin_headers` is a callback function which is
+ * invoked when an incoming header block section is started on a
+ * stream denoted by |stream_id|. Each header field is passed to
+ * application by :type:`nghttp3_recv_header` callback. And then
+ * :type:`nghttp3_end_headers` 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_headers)(nghttp3_conn *conn, int64_t stream_id,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_recv_header` is a callback function which is invoked
+ * when a header field is received on a stream denoted by |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_*.
+ *
+ * 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_header)(nghttp3_conn *conn, int64_t stream_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_headers` is a callback function which is invoked
+ * when an incoming header block 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_headers)(nghttp3_conn *conn, int64_t stream_id,
+ 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
+ * completely. For client, this callback function is invoked when
+ * HTTP response is received completely.
+ *
+ * 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_stream)(nghttp3_conn *conn, int64_t stream_id,
+ void *conn_user_data, void *stream_user_data);
+
+/**
+ * @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
+ * 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.
+ *
+ * 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_send_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|.
+ *
+ * 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);
+
+/**
+ * @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.
+ *
+ * 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);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_callbacks` holds a set of callback functions.
+ */
+typedef struct nghttp3_callbacks {
+ /**
+ * :member:`acked_stream_data` is a callback function which is
+ * invoked when data sent on a particular stream have been
+ * acknowledged by a remote endpoint.
+ */
+ nghttp3_acked_stream_data acked_stream_data;
+ /**
+ * :member:`stream_close` is a callback function which is invoked
+ * when a particular stream has closed.
+ */
+ nghttp3_stream_close stream_close;
+ /**
+ * :member:`recv_data` is a callback function which is invoked when
+ * stream data is received.
+ */
+ nghttp3_recv_data recv_data;
+ /**
+ * :member:`deferred_consume` is a callback function which is
+ * invoked when the library consumed data for a particular stream
+ * which had been blocked for synchronization between streams.
+ */
+ nghttp3_deferred_consume deferred_consume;
+ /**
+ * :member:`begin_headers` is a callback function which is invoked
+ * when a header block has started on a particular stream.
+ */
+ nghttp3_begin_headers begin_headers;
+ /**
+ * :member:`recv_header` is a callback function which is invoked
+ * when a single header field is received on a particular stream.
+ */
+ nghttp3_recv_header recv_header;
+ /**
+ * :member:`end_headers` is a callback function which is invoked
+ * when a header block has ended on a particular stream.
+ */
+ nghttp3_end_headers end_headers;
+ /**
+ * :member:`begin_trailers` is a callback function which is invoked
+ * when a trailer block has started on a particular stream.
+ */
+ nghttp3_begin_headers begin_trailers;
+ /**
+ * :member:`recv_trailer` is a callback function which is invoked
+ * when a single trailer field is received on a particular stream.
+ */
+ nghttp3_recv_header recv_trailer;
+ /**
+ * :member:`end_trailers` is a callback function which is invoked
+ * when a trailer block has ended on a particular stream.
+ */
+ 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.
+ */
+ nghttp3_push_stream push_stream;
+ /**
+ * :member:`end_stream` is a callback function which is invoked when
+ * a receiving side of stream has been closed.
+ */
+ nghttp3_end_stream end_stream;
+ /**
+ * :member:`reset_stream` is a callback function which is invoked
+ * when the library asks application to reset stream (by sending
+ * RESET_STREAM).
+ */
+ nghttp3_reset_stream reset_stream;
+} nghttp3_callbacks;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_settings` defines HTTP/3 settings.
+ */
+typedef struct {
+ /**
+ * :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.
+ */
+ uint64_t max_pushes;
+ /**
+ * :member:`qpack_max_table_capacity` is the maximum size of QPACK
+ * dynamic table.
+ */
+ size_t qpack_max_table_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;
+} nghttp3_settings;
+
+/**
+ * @function
+ *
+ * `nghttp3_settings_default` fills |settings| with the default
+ * values.
+ */
+NGHTTP3_EXTERN void nghttp3_settings_default(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|.
+ */
+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);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_server_new` creates :type:`nghttp3_conn` and
+ * initializes it for server use. The pointer to the object is stored
+ * in |*pconn|.
+ */
+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);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_del` frees resources allocated for |conn|.
+ */
+NGHTTP3_EXTERN void nghttp3_conn_del(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_bind_control_stream` binds stream denoted by
+ * |stream_id| to outgoing unidirectional control stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE`
+ * Control stream has already corresponding stream ID.
+ *
+ * TBD
+ */
+NGHTTP3_EXTERN int nghttp3_conn_bind_control_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_bind_qpack_streams` binds stream denoted by
+ * |qenc_stream_id| to outgoing QPACK encoder stream and stream
+ * denoted by |qdec_stream_id| to outgoing QPACK encoder stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE`
+ * QPACK encoder/decoder stream have already corresponding stream
+ * IDs.
+ *
+ * TBD
+ */
+NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn,
+ int64_t qenc_stream_id,
+ int64_t qdec_stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_read_stream` reads data |src| of length |srclen| on
+ * stream identified by |stream_id|. It returns the number of bytes
+ * consumed. The "consumed" means that application can increase flow
+ * control credit (both stream and connection) of underlying QUIC
+ * connection by that amount. It does not include the amount of data
+ * carried by DATA frame which contains application data (excluding
+ * any control or QPACK unidirectional streams) . See
+ * :type:`nghttp3_recv_data` to handle those bytes. If |fin| is
+ * nonzero, this is the last data from remote endpoint in this stream.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn,
+ int64_t stream_id,
+ const uint8_t *src,
+ size_t srclen, int fin);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_writev_stream` stores stream data to send to |vec| of
+ * length |veccnt| and returns the number of nghttp3_vec object in
+ * which it stored data. It stores stream ID to |*pstream_id|. An
+ * application has to call `nghttp3_conn_add_write_offset` to inform
+ * |conn| of the actual number of bytes that underlying QUIC stack
+ * accepted. |*pfin| will be nonzero if this is the last data to
+ * send. If there is no stream to write data or send fin, this
+ * function returns 0, and -1 is assigned to |*pstream_id|. This
+ * function may return 0 and |*pstream_id| is not -1 and |*pfin| is
+ * nonzero. It means 0 length data to |*pstream_id| and it is the
+ * last data to the stream. They must be passed to QUIC stack, and
+ * they are accepted, the application has to call
+ * `nghttp3_conn_add_write_offset`.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
+ int64_t *pstream_id,
+ int *pfin,
+ nghttp3_vec *vec,
+ size_t veccnt);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_add_write_offset` tells |conn| the number of bytes
+ * |n| for stream denoted by |stream_id| QUIC stack accepted.
+ *
+ * If stream has no data to send but just sends fin (closing the write
+ * side of a stream), the number of bytes sent is 0. It is important
+ * to call this function even if |n| is 0 in this case. It is safe to
+ * call this function if |n| is 0.
+ *
+ * `nghttp3_conn_writev_stream` must be called before calling this
+ * function to get data to send, and those data must be fed into QUIC
+ * stack.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_add_write_offset(nghttp3_conn *conn,
+ int64_t stream_id, size_t n);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_add_ack_offset` tells |conn| the number of bytes |n|
+ * for stream denoted by |stream_id| QUIC stack has acknowledged.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn,
+ int64_t stream_id, uint64_t n);
+
+/**
+ * @function
+ *
+ * `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);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_unblock_stream` tells the library that stream
+ * identified by |stream_id| which was blocked by QUIC flow control is
+ * unblocked.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(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);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_resume_stream` resumes stream identified by
+ * |stream_id| which was previously unable to provide data.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_close_stream` closes stream identified by
+ * |stream_id|. |app_error_code| is the reason of the closure.
+ */
+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:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set.
+ */
+#define NGHTTP3_DATA_FLAG_NONE 0x00
+
+/**
+ * :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
+
+/**
+ * :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
+ * `nghttp3_conn_submit_trailers()`. If
+ * `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
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_max_client_streams_bidi` tells |conn| the
+ * cumulative number of bidirectional streams that client can open.
+ */
+NGHTTP3_EXTERN void
+nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn,
+ uint64_t max_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_max_concurrent_streams` tells |conn| the maximum
+ * number of concurrent streams that a remote endpoint can open,
+ * including both bidirectional and unidirectional streams which
+ * potentially receive QPACK encoded HEADERS frame. This value is
+ * used as a hint to limit the internal resource consumption.
+ */
+NGHTTP3_EXTERN void
+nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn,
+ size_t max_concurrent_streams);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_read_data_callback` is a callback function invoked
+ * when the library asks an application to provide stream data for a
+ * stream denoted by |stream_id|.
+ *
+ * The library provides |vec| of length |veccnt| to the application.
+ * The application should fill data and its length to |vec|. It has
+ * to return the number of the filled objects. The application must
+ * retain data until they are safe to free. It is notified by
+ * :type:`nghttp3_acked_stream_data` callback.
+ *
+ * If this is the last data to send (or there is no data to send
+ * because all data have been sent already), set
+ * :macro:`NGHTTP3_DATA_FLAG_EOF` to |*pflags|.
+ *
+ * If the application is unable to provide data temporarily, return
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK`. When it is ready to provide
+ * data, call `nghttp3_conn_resume_stream()`.
+ *
+ * The callback should return the number of objects in |vec| that the
+ * application filled if it succeeds, or
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ *
+ * TODO Add NGHTTP3_ERR_TEMPORAL_CALLBACK_FAILURE to reset just this
+ * stream.
+ */
+typedef nghttp3_ssize (*nghttp3_read_data_callback)(
+ nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *conn_user_data, void *stream_user_data);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_data_reader` specifies the way how to generate
+ * request or response body.
+ */
+typedef struct {
+ /**
+ * :member:`read_data` is a callback function to generate body.
+ */
+ nghttp3_read_data_callback read_data;
+} nghttp3_data_reader;
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_request` submits HTTP request header fields
+ * and body on the stream identified by |stream_id|. |stream_id| must
+ * be a client initiated bidirectional stream. Only client can submit
+ * HTTP request. |nva| of length |nvlen| specifies HTTP request
+ * header fields. |dr| specifies a request body. If there is no
+ * request body, specify NULL. If |dr| is NULL, it implies the end of
+ * stream. |stream_user_data| is an opaque pointer attached to the
+ * stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_request(
+ nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr, void *stream_user_data);
+
+/**
+ * @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.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_info(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_response` submits HTTP response header fields
+ * and body on the stream identified by |stream_id|. |nva| of length
+ * |nvlen| specifies HTTP response header fields. |dr| specifies a
+ * response body. If there is no response body, specify NULL. If
+ * |dr| is NULL, it implies the end of stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_response(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen,
+ const nghttp3_data_reader *dr);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_trailers` submits HTTP trailer fields on the
+ * stream identified by |stream_id|. |nva| of length |nvlen|
+ * specifies HTTP trailer fields. Calling this function implies the
+ * end of stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen);
+
+/**
+ * @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.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `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.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_stream_user_data` sets |stream_user_data| to the
+ * stream identified by |stream_id|.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn,
+ int64_t stream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `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`.
+ */
+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
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_HIGH` is the highest urgency level.
+ */
+#define NGHTTP3_URGENCY_HIGH 0
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_LOW` is the lowest urgency level.
+ */
+#define NGHTTP3_URGENCY_LOW 7
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_LEVELS` is the number of urgency levels.
+ */
+#define NGHTTP3_URGENCY_LEVELS (NGHTTP3_URGENCY_LOW + 1)
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_pri` represents HTTP priority.
+ */
+typedef struct nghttp3_pri {
+ /**
+ * :member:`urgency` is the urgency of a stream, it must be in
+ * [:macro:`NGHTTP3_URGENCY_HIGH`, :macro:`NGHTTP3_URGENCY_LOW`],
+ * inclusive, and 0 is the highest urgency.
+ */
+ uint32_t urgency;
+ /**
+ * :member:`inc` indicates that a content can be processed
+ * incrementally or not. If inc is 0, it cannot be processed
+ * incrementally. If inc is 1, it can be processed incrementally.
+ * Other value is not permitted.
+ */
+ int inc;
+} 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.
+ *
+ * This function must not be called if |conn| is initialized as
+ * client.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn,
+ nghttp3_pri *dest,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_stream_priority` updates stream priority of a
+ * stream denoted by |stream_id| by the value pointed by |pri|.
+ *
+ * This function must not be called if |conn| is initialized as
+ * client.
+ *
+ * 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_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_set_stream_priority(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_pri *pri);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_is_remote_qpack_encoder_stream` returns nonzero if a
+ * stream denoted by |stream_id| is QPACK encoder stream of a remote
+ * endpoint.
+ */
+NGHTTP3_EXTERN int
+nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `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);
+
+/**
+ * @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
+ *
+ * Because this is a header field name in HTTP/3, the upper cased
+ * alphabet is treated as error.
+ */
+NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len);
+
+/**
+ * @function
+ *
+ * `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
+ */
+NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len);
+
+/**
+ * @function
+ *
+ * `nghttp3_http_parse_priority` parses priority HTTP header field
+ * stored in the buffer pointed by |value| of length |len|. If it
+ * successfully processed header field value, it stores the result
+ * into |*dest|. This function just overwrites what it sees in the
+ * header field value and does not initialize any field in |*dest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * The function could not parse the provided value.
+ */
+NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest,
+ const uint8_t *value,
+ size_t len);
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_VERSION_AGE` is the age of :type:`nghttp3_info`.
+ */
+#define NGHTTP3_VERSION_AGE 1
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_info` is what `nghttp3_version()` returns. It holds
+ * information about the particular nghttp3 version.
+ */
+typedef struct {
+ /**
+ * :member:`age` is the age of this struct. This instance of
+ * nghttp3 sets it to :macro:`NGHTTP3_VERSION_AGE` but a future
+ * version may bump it and add more struct fields at the bottom
+ */
+ int age;
+ /**
+ * :member:`version_num` is the :macro:`NGHTTP3_VERSION_NUM` number
+ * (since age ==1)
+ */
+ int version_num;
+ /**
+ * :member:`version_str` points to the :macro:`NGHTTP3_VERSION`
+ * string (since age ==1)
+ */
+ const char *version_str;
+ /* -------- the above fields all exist when age == 1 */
+} nghttp3_info;
+
+/**
+ * @function
+ *
+ * `nghttp3_version` returns a pointer to a :type:`nghttp3_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.
+ */
+NGHTTP3_EXTERN nghttp3_info *nghttp3_version(int least_version);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGHTTP3_H */
diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h
new file mode 100644
index 0000000000..69d29e9f14
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h
@@ -0,0 +1,46 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 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_VERSION_H
+#define NGHTTP3_VERSION_H
+
+/**
+ * @macro
+ *
+ * Version number of the nghttp3 library release.
+ */
+#define NGHTTP3_VERSION "0.1.0-DEV"
+
+/**
+ * @macro
+ *
+ * Numerical representation of the version number of the nghttp3
+ * library release. This is a 24 bit number with 8 bits for major
+ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3
+ * becomes 0x010203.
+ */
+#define NGHTTP3_VERSION_NUM 0x000100
+
+#endif /* NGHTTP3_VERSION_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c
new file mode 100644
index 0000000000..aae075a73c
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c
@@ -0,0 +1,90 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_buf.h"
+
+void nghttp3_buf_init(nghttp3_buf *buf) {
+ buf->begin = buf->end = buf->pos = buf->last = NULL;
+}
+
+void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len) {
+ buf->begin = buf->pos = buf->last = src;
+ buf->end = buf->begin + len;
+}
+
+void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, buf->begin);
+}
+
+size_t nghttp3_buf_left(const nghttp3_buf *buf) {
+ return (size_t)(buf->end - buf->last);
+}
+
+size_t nghttp3_buf_len(const nghttp3_buf *buf) {
+ return (size_t)(buf->last - buf->pos);
+}
+
+size_t nghttp3_buf_cap(const nghttp3_buf *buf) {
+ return (size_t)(buf->end - buf->begin);
+}
+
+void nghttp3_buf_reset(nghttp3_buf *buf) { buf->pos = buf->last = buf->begin; }
+
+int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem) {
+ uint8_t *p;
+ nghttp3_ssize pos_offset, last_offset;
+
+ if ((size_t)(buf->end - buf->begin) >= size) {
+ return 0;
+ }
+
+ pos_offset = buf->pos - buf->begin;
+ last_offset = buf->last - buf->begin;
+
+ p = nghttp3_mem_realloc(mem, buf->begin, size);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ buf->begin = p;
+ buf->end = p + size;
+ buf->pos = p + pos_offset;
+ buf->last = p + last_offset;
+
+ return 0;
+}
+
+void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b) {
+ nghttp3_buf c = *a;
+
+ *a = *b;
+ *b = c;
+}
+
+void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
+ nghttp3_buf_type type) {
+ tbuf->buf = *buf;
+ tbuf->type = type;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h
new file mode 100644
index 0000000000..472a4b7b14
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h
@@ -0,0 +1,74 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_BUF_H
+#define NGHTTP3_BUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len);
+
+/*
+ * nghttp3_buf_cap returns the capacity of the buffer. In other
+ * words, it returns buf->end - buf->begin.
+ */
+size_t nghttp3_buf_cap(const nghttp3_buf *buf);
+
+int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_buf_swap swaps |a| and |b|.
+ */
+void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b);
+
+typedef enum nghttp3_buf_type {
+ /* NGHTTP3_BUF_TYPE_PRIVATE indicates that memory is allocated for
+ this buffer only and should be freed after its use. */
+ NGHTTP3_BUF_TYPE_PRIVATE,
+ /* NGHTTP3_BUF_TYPE_SHARED indicates that buffer points to shared
+ memory. */
+ NGHTTP3_BUF_TYPE_SHARED,
+ /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a
+ memory which comes from outside of the library. */
+ NGHTTP3_BUF_TYPE_ALIEN,
+} nghttp3_buf_type;
+
+typedef struct nghttp3_typed_buf {
+ nghttp3_buf buf;
+ nghttp3_buf_type type;
+} nghttp3_typed_buf;
+
+void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
+ nghttp3_buf_type type);
+
+void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf);
+
+#endif /* NGHTTP3_BUF_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c
new file mode 100644
index 0000000000..2f9ce7b10c
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c
@@ -0,0 +1,3523 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_conn.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_err.h"
+#include "nghttp3_conv.h"
+#include "nghttp3_http.h"
+
+/*
+ * conn_remote_stream_uni returns nonzero if |stream_id| is remote
+ * unidirectional stream ID.
+ */
+static int conn_remote_stream_uni(nghttp3_conn *conn, int64_t stream_id) {
+ if (conn->server) {
+ return (stream_id & 0x03) == 0x02;
+ }
+ return (stream_id & 0x03) == 0x03;
+}
+
+static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.begin_headers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.begin_headers(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_end_headers(nghttp3_conn *conn, nghttp3_stream *stream) {
+ 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);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_begin_trailers(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.begin_trailers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.begin_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_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream) {
+ 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);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.end_stream) {
+ return 0;
+ }
+
+ rv = conn->callbacks.end_stream(conn, stream->node.nid.id, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ 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) {
+ int rv;
+
+ if (!conn->callbacks.send_stop_sending) {
+ return 0;
+ }
+
+ rv = conn->callbacks.send_stop_sending(conn, stream->node.nid.id,
+ app_error_code, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream,
+ uint64_t app_error_code) {
+ int rv;
+
+ if (!conn->callbacks.reset_stream) {
+ return 0;
+ }
+
+ rv = conn->callbacks.reset_stream(conn, stream->node.nid.id, app_error_code,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ 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) {
+ int rv;
+
+ if (nconsumed == 0 || !conn->callbacks.deferred_consume) {
+ return 0;
+ }
+
+ rv = conn->callbacks.deferred_consume(conn, stream->node.nid.id, nconsumed,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int ricnt_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ nghttp3_stream *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_stream, qpack_blocked_pe);
+ nghttp3_stream *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_stream, qpack_blocked_pe);
+
+ return lhs->qpack_sctx.ricnt < rhs->qpack_sctx.ricnt;
+}
+
+static int cycle_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ const nghttp3_tnode *lhs = nghttp3_struct_of(lhsx, nghttp3_tnode, pe);
+ const nghttp3_tnode *rhs = nghttp3_struct_of(rhsx, nghttp3_tnode, pe);
+
+ if (lhs->cycle == rhs->cycle) {
+ return lhs->seq < rhs->seq;
+ }
+
+ return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP;
+}
+
+static int conn_new(nghttp3_conn **pconn, int server,
+ const nghttp3_callbacks *callbacks,
+ const nghttp3_settings *settings, const nghttp3_mem *mem,
+ void *user_data) {
+ int rv;
+ nghttp3_conn *conn;
+ size_t i;
+
+ 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;
+ }
+
+ rv = nghttp3_map_init(&conn->pushes, mem);
+ if (rv != 0) {
+ goto pushes_init_fail;
+ }
+
+ rv = nghttp3_qpack_decoder_init(&conn->qdec,
+ settings->qpack_max_table_capacity,
+ settings->qpack_blocked_streams, mem);
+ if (rv != 0) {
+ goto qdec_init_fail;
+ }
+
+ rv = nghttp3_qpack_encoder_init(&conn->qenc, 0, 0, mem);
+ if (rv != 0) {
+ goto qenc_init_fail;
+ }
+
+ nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem);
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ 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;
+ }
+
+ conn->callbacks = *callbacks;
+ conn->local.settings = *settings;
+ nghttp3_settings_default(&conn->remote.settings);
+ conn->mem = mem;
+ conn->user_data = user_data;
+ conn->next_seq = 0;
+ 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;
+
+ *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_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 rv;
+
+ rv = conn_new(pconn, /* server = */ 0, callbacks, 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 rv;
+
+ rv = conn_new(pconn, /* server = */ 1, callbacks, settings, mem, user_data);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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);
+
+ (void)ptr;
+
+ nghttp3_stream_del(stream);
+
+ return 0;
+}
+
+void nghttp3_conn_del(nghttp3_conn *conn) {
+ size_t i;
+
+ if (conn == NULL) {
+ return;
+ }
+
+ 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) {
+ nghttp3_pq_free(&conn->sched[i].spq);
+ }
+
+ nghttp3_pq_free(&conn->qpack_blocked_streams);
+
+ 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_mem_free(conn->mem, conn);
+}
+
+nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_stream *stream;
+ size_t bidi_nproc;
+ int rv;
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ /* TODO Assert idtr */
+ /* 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);
+
+ 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;
+ }
+
+ if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
+ conn->tx.goaway_id <= stream_id) {
+ stream->rstate.state = NGHTTP3_REQ_STREAM_STATE_IGN_REST;
+
+ rv = nghttp3_conn_reject_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else {
+ /* unidirectional stream */
+ if (srclen == 0 && fin) {
+ return 0;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ } else if (nghttp3_stream_uni(stream_id)) {
+ if (srclen == 0 && fin) {
+ return 0;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ } else {
+ /* client doesn't expect to receive new bidirectional stream
+ from server. */
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ } else if (conn->server) {
+ if (nghttp3_client_stream_bidi(stream_id)) {
+ if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) {
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ }
+ }
+ } else if (nghttp3_stream_uni(stream_id) &&
+ stream->type == NGHTTP3_STREAM_TYPE_PUSH) {
+ if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) {
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ }
+ }
+
+ if (srclen == 0 && !fin) {
+ return 0;
+ }
+
+ if (nghttp3_stream_uni(stream_id)) {
+ return nghttp3_conn_read_uni(conn, stream, src, srclen, fin);
+ }
+
+ if (fin) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF;
+ }
+ return nghttp3_conn_read_bidi(conn, &bidi_nproc, stream, src, srclen, fin);
+}
+
+static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_stream_read_state *rstate = &stream->rstate;
+ nghttp3_varint_read_state *rvint = &rstate->rvint;
+ nghttp3_ssize nread;
+ int64_t stream_type;
+
+ assert(srclen);
+
+ nread = nghttp3_read_varint(rvint, src, srclen, fin);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ if (rvint->left) {
+ return nread;
+ }
+
+ stream_type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ switch (stream_type) {
+ case NGHTTP3_STREAM_TYPE_CONTROL:
+ if (conn->flags & NGHTTP3_CONN_FLAG_CONTROL_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_CONTROL_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_CONTROL;
+ 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;
+ case NGHTTP3_STREAM_TYPE_QPACK_ENCODER:
+ if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_DECODER:
+ if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER;
+ break;
+ default:
+ stream->type = NGHTTP3_STREAM_TYPE_UNKNOWN;
+ break;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED;
+
+ return nread;
+}
+
+static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_ssize nread = 0;
+ nghttp3_ssize nconsumed = 0;
+ size_t push_nproc;
+ int rv;
+
+ assert(srclen || fin);
+
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) {
+ if (srclen == 0 && fin) {
+ /* Ignore stream if it is closed before reading stream header.
+ If it is closed while reading it, return error, making it
+ consistent in our code base. */
+ if (stream->rstate.rvint.left) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ rv = conn_delete_stream(conn, stream);
+ assert(0 == rv);
+
+ return 0;
+ }
+ nread = conn_read_type(conn, stream, src, srclen, fin);
+ if (nread < 0) {
+ return (int)nread;
+ }
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) {
+ assert((size_t)nread == srclen);
+ return (nghttp3_ssize)srclen;
+ }
+
+ src += nread;
+ srclen -= (size_t)nread;
+
+ if (srclen == 0) {
+ return nread;
+ }
+ }
+
+ switch (stream->type) {
+ case NGHTTP3_STREAM_TYPE_CONTROL:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_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;
+ }
+ nconsumed = nghttp3_conn_read_qpack_encoder(conn, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_DECODER:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+ nconsumed = nghttp3_conn_read_qpack_decoder(conn, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_UNKNOWN:
+ nconsumed = (nghttp3_ssize)srclen;
+
+ rv = conn_call_send_stop_sending(conn, stream,
+ NGHTTP3_H3_STREAM_CREATION_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ default:
+ /* unreachable */
+ assert(0);
+ }
+
+ if (nconsumed < 0) {
+ return nconsumed;
+ }
+
+ return nread + nconsumed;
+}
+
+static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) {
+ return (int64_t)len >= rstate->left;
+}
+
+nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen) {
+ const uint8_t *p = src, *end = src + srclen;
+ 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;
+
+ assert(srclen);
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (rstate->state) {
+ case NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->fr.hd.type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH;
+ if (p == end) {
+ break;
+ }
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->left = rstate->fr.hd.length = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) {
+ if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) {
+ return NGHTTP3_ERR_H3_MISSING_SETTINGS;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_SETTINGS_RECVED;
+ } else if (rstate->fr.hd.type == NGHTTP3_FRAME_SETTINGS) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+
+ 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) {
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS;
+ break;
+ case NGHTTP3_FRAME_GOAWAY:
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_GOAWAY;
+ break;
+ case NGHTTP3_FRAME_MAX_PUSH_ID:
+ 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_MAX_PUSH_ID;
+ break;
+ case NGHTTP3_FRAME_DATA:
+ case NGHTTP3_FRAME_HEADERS:
+ case NGHTTP3_FRAME_PUSH_PROMISE:
+ 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 */
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
+ 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) {
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ /* Read Identifier */
+ 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) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID;
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ /* Read Value */
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ len -= (size_t)nread;
+ if (len == 0) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+ break;
+ }
+
+ 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) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ rv =
+ nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID:
+ 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.settings.iv[0].id = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+
+ if (p == end) {
+ return (nghttp3_ssize)nconsumed;
+ }
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE:
+ 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.settings.iv[0].value = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ rv = nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (rstate->left) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS;
+ break;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_GOAWAY:
+ 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;
+ }
+
+ if (conn->server && !nghttp3_client_stream_bidi(rvint->acc)) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+ if (conn->rx.goaway_id < rvint->acc) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_RECVED;
+ conn->rx.goaway_id = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID:
+ /* server side only */
+ 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;
+ }
+
+ if (conn->local.uni.max_pushes > (uint64_t)rvint->acc) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ conn->local.uni.max_pushes = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ 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) {
+ 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);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)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;
+ 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);
+
+ switch (rstate->fr.hd.type) {
+ case NGHTTP3_FRAME_DATA:
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN);
+ 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;
+ 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;
+ }
+
+ 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);
+ 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 */
+ busy = 1;
+ rstate->state = NGHTTP3_PUSH_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;
+ }
+
+ p += 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;
+ }
+
+ 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 (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;
+ case NGHTTP3_PUSH_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;
+ }
+
+ 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;
+ }
+ }
+
+ *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 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) {
+ rv = conn->callbacks.stream_close(conn, stream->node.nid.id,
+ stream->error_code, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ rv = nghttp3_map_remove(&conn->streams, (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;
+}
+
+static int conn_process_blocked_stream_data(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ nghttp3_buf *buf;
+ size_t nproc;
+ nghttp3_ssize nconsumed;
+ int rv;
+ size_t len;
+
+ 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));
+ }
+ if (nconsumed < 0) {
+ return (int)nconsumed;
+ }
+
+ buf->pos += nproc;
+
+ rv = conn_call_deferred_consume(conn, stream, (size_t)nconsumed);
+ if (rv != 0) {
+ return 0;
+ }
+
+ if (nghttp3_buf_len(buf) == 0) {
+ nghttp3_buf_free(buf, stream->mem);
+ nghttp3_ringbuf_pop_front(&stream->inq);
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
+ break;
+ }
+ }
+
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) &&
+ (stream->flags & NGHTTP3_STREAM_FLAG_CLOSED)) {
+ assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX);
+
+ rv = conn_delete_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen) {
+ nghttp3_ssize nconsumed =
+ nghttp3_qpack_decoder_read_encoder(&conn->qdec, src, srclen);
+ nghttp3_stream *stream;
+ int rv;
+
+ if (nconsumed < 0) {
+ return nconsumed;
+ }
+
+ for (; !nghttp3_pq_empty(&conn->qpack_blocked_streams);) {
+ stream = nghttp3_struct_of(nghttp3_pq_top(&conn->qpack_blocked_streams),
+ nghttp3_stream, qpack_blocked_pe);
+ if (nghttp3_qpack_stream_context_get_ricnt(&stream->qpack_sctx) >
+ nghttp3_qpack_decoder_get_icnt(&conn->qdec)) {
+ break;
+ }
+
+ nghttp3_conn_qpack_blocked_streams_pop(conn);
+ stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED;
+
+ rv = conn_process_blocked_stream_data(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nconsumed;
+}
+
+nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen) {
+ return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen);
+}
+
+static int conn_update_stream_priority(nghttp3_conn *conn,
+ nghttp3_stream *stream, uint8_t pri) {
+ if (stream->node.pri == pri) {
+ return 0;
+ }
+
+ nghttp3_conn_unschedule_stream(conn, stream);
+
+ stream->node.pri = pri;
+
+ assert(nghttp3_stream_bidi_or_push(stream));
+
+ if (nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_schedule_stream(conn, stream);
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_bidi(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;
+ 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_QPACK_DECODE_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_REQ_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;
+ nghttp3_varint_read_state_reset(rvint);
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH;
+ if (p == end) {
+ goto almost_done;
+ }
+ /* Fall through */
+ case NGHTTP3_REQ_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);
+
+ switch (rstate->fr.hd.type) {
+ case NGHTTP3_FRAME_DATA:
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN);
+ 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_REQ_STREAM_STATE_DATA;
+ 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;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = conn_call_begin_headers(conn, stream);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = conn_call_begin_trailers(conn, stream);
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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_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 */
+ busy = 1;
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_FRAME;
+ break;
+ }
+ break;
+ case NGHTTP3_REQ_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;
+ }
+ p += 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_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:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ nread = nghttp3_conn_on_headers(conn, stream, &fake_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;
+ }
+
+ 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);
+ }
+ }
+ }
+
+ 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;
+ }
+
+ 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_REQ_HEADERS_BEGIN:
+ rv = nghttp3_http_on_request_headers(&stream->rx.http);
+ break;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = nghttp3_http_on_response_headers(&stream->rx.http);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = 0;
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+
+ if (rv != 0) {
+ return rv;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ /* Only server utilizes priority information to schedule
+ streams. */
+ if (conn->server) {
+ rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ /* fall through */
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = conn_call_end_headers(conn, stream);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = conn_call_end_trailers(conn, stream);
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+
+ 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;
+ case NGHTTP3_REQ_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;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_REQ_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_REQ_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_REQ_STREAM_STATE_IGN_REST:
+ break;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ }
+
+ *pnproc = (size_t)(p - src);
+ return (nghttp3_ssize)nconsumed;
+}
+
+int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *data, size_t datalen) {
+ int rv;
+
+ rv = nghttp3_http_on_data_chunk(stream, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!conn->callbacks.recv_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_data(conn, stream->node.nid.id, data, datalen,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ 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);
+
+ assert(urgency < NGHTTP3_URGENCY_LEVELS);
+
+ 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;
+ int rv;
+ nghttp3_qpack_decoder *qdec = &conn->qdec;
+ nghttp3_qpack_nv nv;
+ uint8_t flags;
+ nghttp3_buf buf;
+ nghttp3_recv_header recv_header = NULL;
+ nghttp3_http_state *http;
+ int request = 0;
+ int trailers = 0;
+ int ignore_pp = 0;
+
+ if (pp) {
+ 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;
+ }
+
+ nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen);
+ buf.last = buf.end;
+
+ for (;;) {
+ nread = nghttp3_qpack_decoder_read_request(qdec, &stream->qpack_sctx, &nv,
+ &flags, buf.pos,
+ nghttp3_buf_len(&buf), fin);
+
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ buf.pos += nread;
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) {
+ if (conn->local.settings.qpack_blocked_streams <=
+ nghttp3_pq_size(&conn->qpack_blocked_streams)) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED;
+ rv = nghttp3_conn_qpack_blocked_streams_push(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ }
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ nghttp3_qpack_stream_context_reset(&stream->qpack_sctx);
+ break;
+ }
+
+ if (nread == 0) {
+ break;
+ }
+
+ 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);
+ switch (rv) {
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ break;
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ 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);
+ }
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+
+ nghttp3_rcbuf_decref(nv.name);
+ nghttp3_rcbuf_decref(nv.value);
+
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ return buf.pos - src;
+}
+
+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);
+}
+
+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) {
+ case NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE:
+ 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;
+ }
+ rv = nghttp3_qpack_encoder_set_max_dtable_size(&conn->qenc,
+ max_table_capacity);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS:
+ 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;
+ }
+ break;
+ case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH:
+ case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS:
+ case NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE:
+ case NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE:
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ default:
+ /* Ignore unknown settings ID */
+ break;
+ }
+
+ return 0;
+}
+
+static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id,
+ size_t datalen, void *user_data) {
+ nghttp3_conn *conn = stream->conn;
+ int rv;
+
+ if (!conn->callbacks.acked_stream_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.acked_stream_data(conn, stream_id, datalen,
+ conn->user_data, user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+ int rv;
+ nghttp3_stream_callbacks callbacks = {
+ conn_stream_acked_data,
+ };
+
+ rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->conn = conn;
+
+ rv = nghttp3_map_insert(&conn->streams, &stream->me);
+ if (rv != 0) {
+ nghttp3_stream_del(stream);
+ return rv;
+ }
+
+ ++conn->next_seq;
+ *pstream = stream;
+
+ 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);
+}
+
+int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream;
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(!conn->server || nghttp3_server_stream_uni(stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(stream_id));
+
+ if (conn->tx.ctrl) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_CONTROL;
+
+ conn->tx.ctrl = stream;
+
+ rv = nghttp3_stream_write_stream_type(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ frent.aux.settings.local_settings = &conn->local.settings;
+
+ return nghttp3_stream_frq_add(stream, &frent);
+}
+
+int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id,
+ int64_t qdec_stream_id) {
+ nghttp3_stream *stream;
+ int rv;
+
+ assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id));
+ assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(qdec_stream_id));
+
+ if (conn->tx.qenc || conn->tx.qdec) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, qenc_stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+
+ conn->tx.qenc = stream;
+
+ rv = nghttp3_stream_write_stream_type(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, qdec_stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER;
+
+ conn->tx.qdec = stream;
+
+ return nghttp3_stream_write_stream_type(stream);
+}
+
+static nghttp3_ssize conn_writev_stream(nghttp3_conn *conn, int64_t *pstream_id,
+ int *pfin, nghttp3_vec *vec,
+ size_t veccnt, nghttp3_stream *stream) {
+ int rv;
+ nghttp3_ssize n;
+
+ assert(veccnt > 0);
+
+ /* If stream is blocked by read callback, don't attempt to fill
+ more. */
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)) {
+ rv = nghttp3_stream_fill_outq(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (!nghttp3_stream_uni(stream->node.nid.id) && conn->tx.qenc &&
+ !nghttp3_stream_is_blocked(conn->tx.qenc)) {
+ n = nghttp3_stream_writev(conn->tx.qenc, pfin, vec, veccnt);
+ if (n < 0) {
+ return n;
+ }
+ if (n) {
+ *pstream_id = conn->tx.qenc->node.nid.id;
+ return n;
+ }
+ }
+
+ n = nghttp3_stream_writev(stream, pfin, vec, veccnt);
+ if (n < 0) {
+ return n;
+ }
+ /* We might just want to write stream fin without sending any stream
+ data. */
+ if (n == 0 && *pfin == 0) {
+ return 0;
+ }
+
+ *pstream_id = stream->node.nid.id;
+
+ return n;
+}
+
+nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
+ int64_t *pstream_id, int *pfin,
+ nghttp3_vec *vec, size_t veccnt) {
+ nghttp3_ssize ncnt;
+ nghttp3_stream *stream;
+ int rv;
+
+ *pstream_id = -1;
+ *pfin = 0;
+
+ if (veccnt == 0) {
+ return 0;
+ }
+
+ 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) {
+ return ncnt;
+ }
+ }
+
+ if (conn->tx.qdec && !nghttp3_stream_is_blocked(conn->tx.qdec)) {
+ rv = nghttp3_stream_write_qpack_decoder_stream(conn->tx.qdec);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qdec);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ if (conn->tx.qenc && !nghttp3_stream_is_blocked(conn->tx.qenc)) {
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qenc);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ stream = nghttp3_conn_get_next_tx_stream(conn);
+ if (stream == NULL) {
+ return 0;
+ }
+
+ ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, stream);
+ if (ncnt < 0) {
+ return ncnt;
+ }
+
+ if (nghttp3_stream_bidi_or_push(stream) &&
+ !nghttp3_stream_require_schedule(stream)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+
+ return ncnt;
+}
+
+nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) {
+ size_t i;
+ nghttp3_tnode *tnode;
+ nghttp3_pq *pq;
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ pq = &conn->sched[i].spq;
+ if (nghttp3_pq_empty(pq)) {
+ continue;
+ }
+
+ 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);
+ }
+
+ return NULL;
+}
+
+int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id,
+ size_t n) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+ int rv;
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ rv = nghttp3_stream_add_outq_offset(stream, n);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->unscheduled_nwrite += n;
+
+ if (!nghttp3_stream_bidi_or_push(stream)) {
+ return 0;
+ }
+
+ if (!nghttp3_stream_require_schedule(stream)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ return 0;
+ }
+
+ if (stream->unscheduled_nwrite < NGHTTP3_STREAM_MIN_WRITELEN) {
+ return 0;
+ }
+
+ return nghttp3_conn_schedule_stream(conn, stream);
+}
+
+int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t n) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ return nghttp3_stream_add_ack_offset(stream, n);
+}
+
+static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr) {
+ int rv;
+ nghttp3_nv *nnva;
+ nghttp3_frame_entry frent;
+
+ rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ frent.fr.headers.nva = nnva;
+ frent.fr.headers.nvlen = nvlen;
+
+ rv = nghttp3_stream_frq_add(stream, &frent);
+ if (rv != 0) {
+ nghttp3_nva_del(nnva, conn->mem);
+ return rv;
+ }
+
+ if (dr) {
+ frent.fr.hd.type = NGHTTP3_FRAME_DATA;
+ frent.aux.data.dr = *dr;
+
+ rv = nghttp3_stream_frq_add(stream, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_schedule_stream(conn, 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 */
+ int rv;
+
+ rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream),
+ conn_get_sched_pq(conn, &stream->node),
+ stream->unscheduled_nwrite);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->unscheduled_nwrite = 0;
+
+ return 0;
+}
+
+int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) {
+ return 0;
+ }
+
+ return nghttp3_conn_schedule_stream(conn, stream);
+}
+
+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));
+}
+
+int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr,
+ void *stream_user_data) {
+ nghttp3_stream *stream;
+ int rv;
+
+ assert(!conn->server);
+ assert(conn->tx.qenc);
+
+ assert(nghttp3_client_stream_bidi(stream_id));
+
+ /* TODO Should we check that stream_id is client stream_id? */
+ /* TODO Check GOAWAY last stream ID */
+ if (nghttp3_stream_uni(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ 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_IN_USE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ stream->user_data = stream_user_data;
+
+ nghttp3_http_record_request_method(stream, nva, nvlen);
+
+ if (dr == NULL) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, dr);
+}
+
+int nghttp3_conn_submit_info(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send info (non-final response)
+ now. */
+ assert(conn->server);
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, NULL);
+}
+
+int nghttp3_conn_submit_response(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send response now. */
+ assert(conn->server);
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (dr == NULL) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, dr);
+}
+
+int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send trailer now. */
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+
+ 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;
+
+ assert(conn->tx.ctrl);
+
+ frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ frent.fr.goaway.id = conn->server ? (1ull << 62) - 4 : (1ull << 62) - 1;
+
+ assert(frent.fr.goaway.id <= conn->tx.goaway_id);
+
+ rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->tx.goaway_id = frent.fr.goaway.id;
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED;
+
+ return 0;
+}
+
+int nghttp3_conn_shutdown(nghttp3_conn *conn) {
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(conn->tx.ctrl);
+
+ frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ if (conn->server) {
+ 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);
+ }
+
+ assert(frent.fr.goaway.id <= conn->tx.goaway_id);
+
+ rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->tx.goaway_id = frent.fr.goaway.id;
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED;
+
+ return 0;
+}
+
+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);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED;
+ stream->unscheduled_nwrite = 0;
+
+ if (nghttp3_stream_bidi_or_push(stream)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+
+ return 0;
+}
+
+int 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;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR;
+ stream->unscheduled_nwrite = 0;
+
+ if (nghttp3_stream_bidi_or_push(stream)) {
+ 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;
+ }
+
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED;
+
+ if (nghttp3_stream_bidi_or_push(stream) &&
+ nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_ensure_stream_scheduled(conn, stream);
+ }
+
+ return 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;
+ }
+
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED;
+
+ if (nghttp3_stream_bidi_or_push(stream) &&
+ nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_ensure_stream_scheduled(conn, stream);
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (nghttp3_stream_uni(stream_id) &&
+ stream->type != NGHTTP3_STREAM_TYPE_PUSH &&
+ stream->type != NGHTTP3_STREAM_TYPE_UNKNOWN) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+
+ stream->error_code = app_error_code;
+
+ 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))) {
+ return conn_delete_stream(conn, stream);
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_CLOSED;
+ return 0;
+}
+
+int nghttp3_conn_reset_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_RESET;
+ }
+ return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id);
+}
+
+int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX);
+
+ return nghttp3_pq_push(&conn->qpack_blocked_streams,
+ &stream->qpack_blocked_pe);
+}
+
+void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn) {
+ assert(!nghttp3_pq_empty(&conn->qpack_blocked_streams));
+ nghttp3_pq_pop(&conn->qpack_blocked_streams);
+}
+
+void nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn,
+ uint64_t max_streams) {
+ assert(conn->server);
+ assert(conn->remote.bidi.max_client_streams <= max_streams);
+
+ conn->remote.bidi.max_client_streams = max_streams;
+}
+
+void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn,
+ size_t max_concurrent_streams) {
+ nghttp3_qpack_decoder_set_max_concurrent_streams(&conn->qdec,
+ 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);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ stream->user_data = stream_user_data;
+
+ return 0;
+}
+
+int64_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 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);
+
+ assert(conn->server);
+
+ 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);
+
+ 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);
+
+ assert(conn->server);
+ assert(pri->urgency < NGHTTP3_URGENCY_LEVELS);
+ assert(pri->inc == 0 || pri->inc == 1);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ stream->rx.http.pri = nghttp3_pri_to_uint8(pri);
+
+ return conn_update_stream_priority(conn, stream, stream->rx.http.pri);
+}
+
+int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ if (!conn_remote_stream_uni(conn, stream_id)) {
+ return 0;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+}
+
+void nghttp3_settings_default(nghttp3_settings *settings) {
+ 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);
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h
new file mode 100644
index 0000000000..f0f012e177
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h
@@ -0,0 +1,289 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_CONN_H
+#define NGHTTP3_CONN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_stream.h"
+#include "nghttp3_map.h"
+#include "nghttp3_qpack.h"
+#include "nghttp3_tnode.h"
+#include "nghttp3_idtr.h"
+#include "nghttp3_gaptr.h"
+
+#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1)
+
+/* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic
+ table size for QPACK encoder. */
+#define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384
+
+/* NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS is the maximum number of
+ 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
+/* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has
+ been received. */
+#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001
+/* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has
+ opened. */
+#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002
+/* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder
+ stream has opened. */
+#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004
+/* 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
+/* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has
+ received. */
+#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020
+/* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has
+ been submitted for transmission. */
+#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040
+
+struct nghttp3_conn {
+ nghttp3_callbacks callbacks;
+ nghttp3_map streams;
+ nghttp3_map pushes;
+ nghttp3_qpack_decoder qdec;
+ nghttp3_qpack_encoder qenc;
+ nghttp3_pq qpack_blocked_streams;
+ struct {
+ nghttp3_pq spq;
+ } sched[NGHTTP3_URGENCY_LEVELS];
+ const nghttp3_mem *mem;
+ void *user_data;
+ int server;
+ uint16_t flags;
+ uint64_t next_seq;
+
+ struct {
+ nghttp3_settings settings;
+ struct {
+ /* max_pushes is the number of push IDs that local endpoint can
+ issue. This field is used by server only. */
+ 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;
+
+ struct {
+ struct {
+ nghttp3_idtr idtr;
+ /* max_client_streams is the cumulative number of client
+ initiated bidirectional stream ID the remote endpoint can
+ 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;
+
+ struct {
+ /* goaway_id is the latest ID received in GOAWAY frame. */
+ int64_t goaway_id;
+
+ int64_t max_stream_id_bidi;
+ int64_t max_push_id;
+ } rx;
+
+ struct {
+ struct {
+ nghttp3_buf rbuf;
+ nghttp3_buf ebuf;
+ } qpack;
+ nghttp3_stream *ctrl;
+ nghttp3_stream *qenc;
+ nghttp3_stream *qdec;
+ /* goaway_id is the latest ID sent in GOAWAY frame. */
+ int64_t goaway_id;
+ } tx;
+};
+
+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);
+
+nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen, int fin);
+
+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);
+
+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);
+
+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);
+
+int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
+ const nghttp3_frame_settings *fr);
+
+int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
+ nghttp3_stream *stream);
+
+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,
+ nghttp3_stream *stream);
+
+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
new file mode 100644
index 0000000000..04bf6a0f29
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c
@@ -0,0 +1,134 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_conv.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_str.h"
+
+int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) {
+ union {
+ char b[8];
+ uint16_t n16;
+ uint32_t n32;
+ uint64_t n64;
+ } n;
+
+ *plen = 1u << (*p >> 6);
+
+ switch (*plen) {
+ case 1:
+ return (int64_t)*p;
+ case 2:
+ memcpy(&n, p, 2);
+ n.b[0] &= 0x3f;
+ return (int64_t)ntohs(n.n16);
+ case 4:
+ memcpy(&n, p, 4);
+ n.b[0] &= 0x3f;
+ return (int64_t)ntohl(n.n32);
+ case 8:
+ memcpy(&n, p, 8);
+ n.b[0] &= 0x3f;
+ return (int64_t)nghttp3_ntohl64(n.n64);
+ }
+
+ assert(0);
+}
+
+int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; }
+
+size_t nghttp3_get_varint_len(const uint8_t *p) { return 1u << (*p >> 6); }
+
+uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n) {
+ n = nghttp3_htonl64(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n) {
+ n = nghttp3_htonl64(n);
+ return nghttp3_cpymem(p, ((const uint8_t *)&n) + 2, 6);
+}
+
+uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n) {
+ n = htonl(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n) {
+ n = htonl(n);
+ return nghttp3_cpymem(p, ((const uint8_t *)&n) + 1, 3);
+}
+
+uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n) {
+ n = htons(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n) {
+ uint8_t *rv;
+ if (n < 64) {
+ *p++ = (uint8_t)n;
+ return p;
+ }
+ if (n < 16384) {
+ rv = nghttp3_put_uint16be(p, (uint16_t)n);
+ *p |= 0x40;
+ return rv;
+ }
+ if (n < 1073741824) {
+ rv = nghttp3_put_uint32be(p, (uint32_t)n);
+ *p |= 0x80;
+ return rv;
+ }
+ assert(n < 4611686018427387904LL);
+ rv = nghttp3_put_uint64be(p, (uint64_t)n);
+ *p |= 0xc0;
+ return rv;
+}
+
+size_t nghttp3_put_varint_len(int64_t n) {
+ if (n < 64) {
+ return 1;
+ }
+ if (n < 16384) {
+ return 2;
+ }
+ if (n < 1073741824) {
+ return 4;
+ }
+ assert(n < 4611686018427387904LL);
+ return 8;
+}
+
+uint64_t nghttp3_ord_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2) + 1;
+}
+
+uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri) {
+ return (uint8_t)((uint32_t)pri->inc << 7 | pri->urgency);
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h
new file mode 100644
index 0000000000..860326385b
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h
@@ -0,0 +1,221 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_CONV_H
+#define NGHTTP3_CONV_H
+
+#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 <nghttp3/nghttp3.h>
+
+#if defined(HAVE_BSWAP_64) || \
+ (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
+# define nghttp3_bswap64 bswap_64
+#else /* !HAVE_BSWAP_64 */
+# define nghttp3_bswap64(N) \
+ ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
+#endif /* !HAVE_BSWAP_64 */
+
+#if defined(HAVE_BE64TOH) || \
+ (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
+# define nghttp3_ntohl64(N) be64toh(N)
+# define nghttp3_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+# if defined(WORDS_BIGENDIAN)
+# define nghttp3_ntohl64(N) (N)
+# define nghttp3_htonl64(N) (N)
+# else /* !WORDS_BIGENDIAN */
+# define nghttp3_ntohl64(N) nghttp3_bswap64(N)
+# define nghttp3_htonl64(N) nghttp3_bswap64(N)
+# endif /* !WORDS_BIGENDIAN */
+#endif /* !HAVE_BE64TOH */
+
+#if defined(WIN32)
+/* Windows requires ws2_32 library for ntonl family of functions. We
+ define inline functions for those functions so that we don't have
+ dependency on that lib. */
+
+# ifdef _MSC_VER
+# define STIN static __inline
+# else
+# define STIN static inline
+# endif
+
+STIN uint32_t 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 htons(uint16_t hostshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = hostshort >> 8;
+ *p = hostshort & 0xffu;
+ return res;
+}
+
+STIN uint32_t 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 ntohs(uint16_t netshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&netshort;
+ res = *p++ << 8;
+ res += *p;
+ return res;
+}
+
+#endif /* WIN32 */
+
+/*
+ * nghttp3_get_varint reads variable-length integer from |p|, and
+ * returns it in host byte order. The number of bytes read is stored
+ * in |*plen|.
+ */
+int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p);
+
+/*
+ * nghttp3_get_varint_fb reads first byte of encoded variable-length
+ * integer from |p|.
+ */
+int64_t nghttp3_get_varint_fb(const uint8_t *p);
+
+/*
+ * nghttp3_get_varint_len returns the required number of bytes to read
+ * variable-length integer starting at |p|.
+ */
+size_t nghttp3_get_varint_len(const uint8_t *p);
+
+/*
+ * nghttp3_put_uint64be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n);
+
+/*
+ * nghttp3_put_uint48be writes |n| in host byte order in |p| in
+ * network byte order. It writes only least significant 48 bits. It
+ * returns the one beyond of the last written position.
+ */
+uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n);
+
+/*
+ * nghttp3_put_uint32be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n);
+
+/*
+ * nghttp3_put_uint24be writes |n| in host byte order in |p| in
+ * network byte order. It writes only least significant 24 bits. It
+ * returns the one beyond of the last written position.
+ */
+uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n);
+
+/*
+ * nghttp3_put_uint16be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n);
+
+/*
+ * nghttp3_put_varint writes |n| in |p| using variable-length integer
+ * encoding. It returns the one beyond of the last written position.
+ */
+uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n);
+
+/*
+ * nghttp3_put_varint_len returns the required number of bytes to
+ * encode |n|.
+ */
+size_t nghttp3_put_varint_len(int64_t n);
+
+/*
+ * nghttp3_ord_stream_id returns the ordinal number of |stream_id|.
+ */
+uint64_t nghttp3_ord_stream_id(int64_t stream_id);
+
+/*
+ * NGHTTP3_PRI_INC_MASK is a bit mask to retrieve incremental bit from
+ * a value produced by nghttp3_pri_to_uint8.
+ */
+#define NGHTTP3_PRI_INC_MASK (1 << 7)
+
+/*
+ * nghttp3_pri_to_uint8 encodes |pri| into uint8_t variable.
+ */
+uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri);
+
+/*
+ * nghttp3_pri_uint8_urgency extracts urgency from |PRI| which is
+ * supposed to be constructed by nghttp3_pri_to_uint8.
+ */
+#define nghttp3_pri_uint8_urgency(PRI) \
+ ((uint32_t)((PRI) & ~NGHTTP3_PRI_INC_MASK))
+
+/*
+ * nghttp3_pri_uint8_inc extracts inc from |PRI| which is supposed to
+ * be constructed by nghttp3_pri_to_uint8.
+ */
+#define nghttp3_pri_uint8_inc(PRI) (((PRI)&NGHTTP3_PRI_INC_MASK) != 0)
+
+#endif /* NGHTTP3_CONV_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_debug.c b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.c
new file mode 100644
index 0000000000..4021b0dc46
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.c
@@ -0,0 +1,61 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_debug.h"
+
+#include <stdio.h>
+
+#ifdef DEBUGBUILD
+
+static void nghttp3_default_debug_vfprintf_callback(const char *fmt,
+ va_list args) {
+ vfprintf(stderr, fmt, args);
+}
+
+static nghttp3_debug_vprintf_callback static_debug_vprintf_callback =
+ nghttp3_default_debug_vfprintf_callback;
+
+void nghttp3_debug_vprintf(const char *format, ...) {
+ if (static_debug_vprintf_callback) {
+ va_list args;
+ va_start(args, format);
+ static_debug_vprintf_callback(format, args);
+ va_end(args);
+ }
+}
+
+void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback) {
+ static_debug_vprintf_callback = debug_vprintf_callback;
+}
+
+#else /* !DEBUGBUILD */
+
+void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback) {
+ (void)debug_vprintf_callback;
+}
+
+#endif /* !DEBUGBUILD */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_debug.h b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.h
new file mode 100644
index 0000000000..01ed918414
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.h
@@ -0,0 +1,44 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_DEBUG_H
+#define NGHTTP3_DEBUG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#ifdef DEBUGBUILD
+# define DEBUGF(...) nghttp3_debug_vprintf(__VA_ARGS__)
+void nghttp3_debug_vprintf(const char *format, ...);
+#else
+# define DEBUGF(...) \
+ do { \
+ } while (0)
+#endif
+
+#endif /* NGHTTP3_DEBUG_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c
new file mode 100644
index 0000000000..fb1353ea8a
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c
@@ -0,0 +1,126 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_err.h"
+
+const char *nghttp3_strerror(int liberr) {
+ switch (liberr) {
+ case NGHTTP3_ERR_INVALID_ARGUMENT:
+ return "ERR_INVALID_ARGUMENT";
+ case NGHTTP3_ERR_NOBUF:
+ return "ERR_NOBUF";
+ case NGHTTP3_ERR_INVALID_STATE:
+ return "ERR_INVALID_STATE";
+ case NGHTTP3_ERR_WOULDBLOCK:
+ 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:
+ return "ERR_REMOVE_HTTP_HEADER";
+ case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING:
+ return "ERR_MALFORMED_HTTP_MESSAGING";
+ case NGHTTP3_ERR_QPACK_FATAL:
+ 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_QPACK_DECOMPRESSION_FAILED:
+ return "ERR_QPACK_DECOMPRESSION_FAILED";
+ case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
+ return "ERR_QPACK_ENCODER_STREAM_ERROR";
+ case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR:
+ return "ERR_QPACK_DECODER_STREAM_ERROR";
+ case NGHTTP3_ERR_H3_FRAME_UNEXPECTED:
+ return "ERR_H3_FRAME_UNEXPECTED";
+ case NGHTTP3_ERR_H3_FRAME_ERROR:
+ return "ERR_H3_FRAME_ERROR";
+ case NGHTTP3_ERR_H3_MISSING_SETTINGS:
+ return "ERR_H3_MISSING_SETTINGS";
+ case NGHTTP3_ERR_H3_INTERNAL_ERROR:
+ return "ERR_H3_INTERNAL_ERROR";
+ case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
+ return "ERR_CLOSED_CRITICAL_STREAM";
+ case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR:
+ return "ERR_H3_GENERAL_PROTOCOL_ERROR";
+ case NGHTTP3_ERR_H3_ID_ERROR:
+ return "ERR_H3_ID_ERROR";
+ case NGHTTP3_ERR_H3_SETTINGS_ERROR:
+ return "ERR_H3_SETTINGS_ERROR";
+ case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR:
+ return "ERR_H3_STREAM_CREATION_ERROR";
+ case NGHTTP3_ERR_NOMEM:
+ return "ERR_NOMEM";
+ case NGHTTP3_ERR_CALLBACK_FAILURE:
+ return "ERR_CALLBACK_FAILURE";
+ default:
+ return "(unknown)";
+ }
+}
+
+uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) {
+ switch (liberr) {
+ case 0:
+ return NGHTTP3_H3_NO_ERROR;
+ case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED:
+ return NGHTTP3_QPACK_DECOMPRESSION_FAILED;
+ case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
+ return NGHTTP3_QPACK_ENCODER_STREAM_ERROR;
+ case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR:
+ return NGHTTP3_QPACK_DECODER_STREAM_ERROR;
+ case NGHTTP3_ERR_H3_FRAME_UNEXPECTED:
+ return NGHTTP3_H3_FRAME_UNEXPECTED;
+ case NGHTTP3_ERR_H3_FRAME_ERROR:
+ return NGHTTP3_H3_FRAME_ERROR;
+ case NGHTTP3_ERR_H3_MISSING_SETTINGS:
+ return NGHTTP3_H3_MISSING_SETTINGS;
+ case NGHTTP3_ERR_H3_INTERNAL_ERROR:
+ return NGHTTP3_H3_INTERNAL_ERROR;
+ case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
+ return NGHTTP3_H3_CLOSED_CRITICAL_STREAM;
+ case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR:
+ return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
+ case NGHTTP3_ERR_H3_ID_ERROR:
+ return NGHTTP3_H3_ID_ERROR;
+ case NGHTTP3_ERR_H3_SETTINGS_ERROR:
+ return NGHTTP3_H3_SETTINGS_ERROR;
+ case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR:
+ return NGHTTP3_H3_STREAM_CREATION_ERROR;
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING:
+ return NGHTTP3_H3_MESSAGE_ERROR;
+ default:
+ return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
+ }
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_err.h b/deps/ngtcp2/nghttp3/lib/nghttp3_err.h
new file mode 100644
index 0000000000..2fa914f86b
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_err.h
@@ -0,0 +1,34 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_ERR_H
+#define NGHTTP3_ERR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#endif /* NGHTTP3_ERR_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c
new file mode 100644
index 0000000000..6be82c6eda
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c
@@ -0,0 +1,218 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_frame.h"
+
+#include <string.h>
+
+#include "nghttp3_conv.h"
+#include "nghttp3_str.h"
+
+uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) {
+ p = nghttp3_put_varint(p, hd->type);
+ p = nghttp3_put_varint(p, hd->length);
+ return p;
+}
+
+size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) {
+ return nghttp3_put_varint_len(hd->type) + nghttp3_put_varint_len(hd->length);
+}
+
+uint8_t *nghttp3_frame_write_settings(uint8_t *p,
+ const nghttp3_frame_settings *fr) {
+ size_t i;
+
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+
+ for (i = 0; i < fr->niv; ++i) {
+ p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id);
+ p = nghttp3_put_varint(p, (int64_t)fr->iv[i].value);
+ }
+
+ return p;
+}
+
+size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen,
+ const nghttp3_frame_settings *fr) {
+ size_t payloadlen = 0;
+ size_t i;
+
+ for (i = 0; i < fr->niv; ++i) {
+ payloadlen += nghttp3_put_varint_len((int64_t)fr->iv[i].id) +
+ nghttp3_put_varint_len((int64_t)fr->iv[i].value);
+ }
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varint_len(NGHTTP3_FRAME_SETTINGS) +
+ nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
+}
+
+uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p,
+ const nghttp3_frame_cancel_push *fr) {
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+ p = nghttp3_put_varint(p, fr->push_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);
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) +
+ 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) {
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+ p = nghttp3_put_varint(p, fr->push_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);
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ 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);
+
+ 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);
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) +
+ nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
+}
+
+int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_mem *mem) {
+ size_t i;
+ uint8_t *data = NULL;
+ size_t buflen = 0;
+ nghttp3_nv *p;
+
+ if (nvlen == 0) {
+ *pnva = NULL;
+
+ return 0;
+ }
+
+ for (i = 0; i < nvlen; ++i) {
+ /* + 1 for null-termination */
+ if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) == 0) {
+ buflen += nva[i].namelen + 1;
+ }
+ if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) == 0) {
+ buflen += nva[i].valuelen + 1;
+ }
+ }
+
+ buflen += sizeof(nghttp3_nv) * nvlen;
+
+ *pnva = nghttp3_mem_malloc(mem, buflen);
+
+ if (*pnva == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ p = *pnva;
+ data = (uint8_t *)(*pnva) + sizeof(nghttp3_nv) * nvlen;
+
+ for (i = 0; i < nvlen; ++i) {
+ p->flags = nva[i].flags;
+
+ if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) {
+ p->name = nva[i].name;
+ p->namelen = nva[i].namelen;
+ } else {
+ if (nva[i].namelen) {
+ memcpy(data, nva[i].name, nva[i].namelen);
+ }
+ p->name = data;
+ p->namelen = nva[i].namelen;
+ data[p->namelen] = '\0';
+ nghttp3_downcase(p->name, p->namelen);
+ data += nva[i].namelen + 1;
+ }
+
+ if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) {
+ p->value = nva[i].value;
+ p->valuelen = nva[i].valuelen;
+ } else {
+ if (nva[i].valuelen) {
+ memcpy(data, nva[i].value, nva[i].valuelen);
+ }
+ p->value = data;
+ p->valuelen = nva[i].valuelen;
+ data[p->valuelen] = '\0';
+ data += nva[i].valuelen + 1;
+ }
+
+ ++p;
+ }
+ return 0;
+}
+
+void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, nva);
+}
+
+void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
+ const nghttp3_mem *mem) {
+ if (fr == NULL) {
+ return;
+ }
+
+ 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
new file mode 100644
index 0000000000..9bd59a9660
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h
@@ -0,0 +1,243 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_FRAME_H
+#define NGHTTP3_FRAME_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_buf.h"
+
+typedef enum nghttp3_frame_type {
+ NGHTTP3_FRAME_DATA = 0x00,
+ NGHTTP3_FRAME_HEADERS = 0x01,
+ NGHTTP3_FRAME_CANCEL_PUSH = 0x03,
+ NGHTTP3_FRAME_SETTINGS = 0x04,
+ NGHTTP3_FRAME_PUSH_PROMISE = 0x05,
+ NGHTTP3_FRAME_GOAWAY = 0x07,
+ NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d,
+} nghttp3_frame_type;
+
+typedef enum nghttp3_h2_reserved_type {
+ NGHTTP3_H2_FRAME_PRIORITY = 0x02,
+ NGHTTP3_H2_FRAME_PING = 0x06,
+ NGHTTP3_H2_FRAME_WINDOW_UPDATE = 0x08,
+ NGHTTP3_H2_FRAME_CONTINUATION = 0x9,
+} nghttp3_h2_reserved_type;
+
+typedef struct nghttp3_frame_hd {
+ int64_t type;
+ int64_t length;
+} nghttp3_frame_hd;
+
+typedef struct nghttp3_frame_data {
+ nghttp3_frame_hd hd;
+} nghttp3_frame_data;
+
+typedef struct nghttp3_frame_headers {
+ nghttp3_frame_hd hd;
+ nghttp3_nv *nva;
+ 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_H2_SETTINGS_ID_ENABLE_PUSH 0x2
+#define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3
+#define NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE 0x4
+#define NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE 0x5
+
+typedef struct nghttp3_settings_entry {
+ uint64_t id;
+ uint64_t value;
+} nghttp3_settings_entry;
+
+typedef struct nghttp3_frame_settings {
+ nghttp3_frame_hd hd;
+ size_t niv;
+ 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 {
+ nghttp3_frame_hd hd;
+ int64_t push_id;
+} nghttp3_frame_max_push_id;
+
+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;
+
+/*
+ * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This
+ * function assumes that |dest| has enough space to write |hd|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd);
+
+/*
+ * nghttp3_frame_write_hd_len returns the number of bytes required to
+ * write |hd|. hd->length must be set.
+ */
+size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd);
+
+/*
+ * nghttp3_frame_write_settings writes SETTINGS 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_settings(uint8_t *dest,
+ const nghttp3_frame_settings *fr);
+
+/*
+ * nghttp3_frame_write_settings_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_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|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_goaway(uint8_t *dest,
+ const nghttp3_frame_goaway *fr);
+
+/*
+ * nghttp3_frame_write_goaway_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_goaway_len(int64_t *ppayloadlen,
+ const nghttp3_frame_goaway *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
+ * nghttp2_nv are guaranteed to be NULL-terminated even if the input
+ * is not null-terminated.
+ *
+ * The |*pnva| must be freed using nghttp3_nva_del().
+ *
+ * This function returns 0 if it succeeds or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_nva_del frees |nva|.
+ */
+void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_frame_headers_free frees memory allocated for |fr|. It
+ * assumes that fr->nva is created by nghttp3_nva_copy() or NULL.
+ */
+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
new file mode 100644
index 0000000000..c60887d1ec
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c
@@ -0,0 +1,130 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "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};
+
+ rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar,
+ sizeof(nghttp3_range), mem);
+ if (rv != 0) {
+ return 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;
+}
+
+void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) {
+ if (gaptr == NULL) {
+ return;
+ }
+
+ nghttp3_ksl_free(&gaptr->gap);
+}
+
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) {
+ int rv;
+ nghttp3_range k, m, l, r, q = {offset, offset + datalen};
+ nghttp3_ksl_it it;
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+
+ for (; !nghttp3_ksl_it_end(&it);) {
+ k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+ m = nghttp3_range_intersect(&q, &k);
+ if (!nghttp3_range_len(&m)) {
+ break;
+ }
+
+ if (nghttp3_range_eq(&k, &m)) {
+ nghttp3_ksl_remove(&gaptr->gap, &it, &k);
+ continue;
+ }
+ nghttp3_range_cut(&l, &r, &k, &m);
+ if (nghttp3_range_len(&l)) {
+ nghttp3_ksl_update_key(&gaptr->gap, &k, &l);
+
+ if (nghttp3_range_len(&r)) {
+ rv = nghttp3_ksl_insert(&gaptr->gap, &it, &r, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else if (nghttp3_range_len(&r)) {
+ nghttp3_ksl_update_key(&gaptr->gap, &k, &r);
+ }
+ nghttp3_ksl_it_next(&it);
+ }
+ return 0;
+}
+
+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);
+ return r.begin;
+}
+
+nghttp3_ksl_it 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);
+}
+
+int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
+ size_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);
+ 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_range r;
+
+ assert(!nghttp3_ksl_it_end(&it));
+
+ r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+
+ nghttp3_ksl_remove(&gaptr->gap, NULL, &r);
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h
new file mode 100644
index 0000000000..b1713a048b
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h
@@ -0,0 +1,104 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_GAPTR_H
+#define NGHTTP3_GAPTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_ksl.h"
+
+/*
+ * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX).
+ */
+typedef struct nghttp3_gaptr {
+ /* gap maintains the range of offset which is not received
+ yet. Initially, its range is [0, UINT64_MAX). */
+ nghttp3_ksl gap;
+ /* mem is custom memory allocator */
+ const nghttp3_mem *mem;
+} 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);
+
+/*
+ * nghttp3_gaptr_free frees resources allocated for |gaptr|.
+ */
+void nghttp3_gaptr_free(nghttp3_gaptr *gaptr);
+
+/*
+ * nghttp3_gaptr_push adds new data of length |datalen| at the stream
+ * offset |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory
+ */
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen);
+
+/*
+ * nghttp3_gaptr_first_gap_offset returns the offset to the first gap.
+ * If there is no gap, it returns UINT64_MAX.
+ */
+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_ksl_it 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);
+
+/*
+ * nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if
+ * the range is pushed. This function assumes that at least one gap
+ * exists.
+ */
+void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr);
+
+#endif /* NGHTTP3_GAPTR_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c
new file mode 100644
index 0000000000..465c36ab2c
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c
@@ -0,0 +1,844 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2015 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_http.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_stream.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_conv.h"
+
+static uint8_t downcase(uint8_t c) {
+ return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
+}
+
+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])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
+
+static int64_t parse_uint(const uint8_t *s, size_t len) {
+ int64_t n = 0;
+ size_t i;
+ if (len == 0) {
+ return -1;
+ }
+ for (i = 0; i < len; ++i) {
+ if ('0' <= s[i] && s[i] <= '9') {
+ if (n > INT64_MAX / 10) {
+ return -1;
+ }
+ n *= 10;
+ if (n > INT64_MAX - (s[i] - '0')) {
+ return -1;
+ }
+ n += s[i] - '0';
+ continue;
+ }
+ return -1;
+ }
+ 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)) {
+ return 0;
+ }
+ http->flags = (uint16_t)(http->flags | flag);
+ return 1;
+}
+
+static int expect_response_body(nghttp3_http_state *http) {
+ return (http->flags & NGHTTP3_HTTP_FLAG_METH_HEAD) == 0 &&
+ http->status_code / 100 != 1 && http->status_code != 304 &&
+ http->status_code != 204;
+}
+
+/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
+ header field to represent system-wide OPTIONS request. Otherwise,
+ :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) {
+ 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;
+
+ for (;;) {
+ for (; p != end && (*p == ' ' || *p == '\t'); ++p)
+ ;
+
+ if (p == end) {
+ break;
+ }
+
+ switch (*p) {
+ case 'u':
+ ++p;
+
+ if (p + 2 > end || *p++ != '=') {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!('0' <= *p && *p <= '7')) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.urgency = (uint32_t)(*p++ - '0');
+
+ if (p == end) {
+ goto fin;
+ }
+
+ if (*p++ != ',') {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ break;
+ case 'i':
+ ++p;
+
+ if (p == end) {
+ pri.inc = 1;
+ goto fin;
+ }
+
+ if (*p == ',') {
+ pri.inc = 1;
+ ++p;
+ break;
+ }
+
+ if (p + 3 > end || *p != '=' || *(p + 1) != '?' ||
+ (*(p + 2) != '0' && *(p + 2) != '1')) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.inc = *(p + 2) == '1';
+
+ p += 3;
+
+ if (p == end) {
+ goto fin;
+ }
+
+ if (*p++ != ',') {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ break;
+ default:
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (p == end) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ }
+
+fin:
+
+ *dest = pri;
+
+ return 0;
+}
+
+static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type,
+ nghttp3_qpack_nv *nv, int trailers,
+ int connect_protocol) {
+ nghttp3_pri pri;
+
+ if (nv->name->base[0] == ':') {
+ if (trailers ||
+ (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__AUTHORITY:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__METHOD:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__METHOD)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ switch (nv->value->len) {
+ case 4:
+ if (lstreq("HEAD", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
+ }
+ break;
+ case 7:
+ 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;
+ case 'S':
+ if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_OPTIONS;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PATH)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (nv->value->base[0] == '/') {
+ http->flags |= NGHTTP3_HTTP_FLAG_PATH_REGULAR;
+ } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
+ http->flags |= NGHTTP3_HTTP_FLAG_PATH_ASTERISK;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__SCHEME:
+ 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))) {
+ http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ if (!connect_protocol) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
+ /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
+ MUST NOT generate a trailer that contains a field necessary for
+ message framing (e.g., Transfer-Encoding and Content-Length),
+ ... */
+ if (trailers) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->content_length != -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = parse_uint(nv->value->base, nv->value->len);
+ if (http->content_length == -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ /* disallowed header fields */
+ case NGHTTP3_QPACK_TOKEN_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
+ case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
+ case NGHTTP3_QPACK_TOKEN_UPGRADE:
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ 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);
+ }
+ break;
+ default:
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+static int http_response_on_header(nghttp3_http_state *http,
+ nghttp3_qpack_nv *nv, int trailers) {
+ if (nv->name->base[0] == ':') {
+ if (trailers ||
+ (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__STATUS: {
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (nv->value->len != 3) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
+ if (http->status_code < 100 || http->status_code == 101) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
+ /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
+ MUST NOT generate a trailer that contains a field necessary for
+ message framing (e.g., Transfer-Encoding and Content-Length),
+ ... */
+ if (trailers) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->status_code == 204) {
+ /* content-length header field in 204 response is prohibited by
+ RFC 7230. But some widely used servers send content-length:
+ 0. Until they get fixed, we ignore it. */
+ if (http->content_length != -1) {
+ /* Found multiple content-length field */
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (!lstrieq("0", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = 0;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->status_code / 100 == 1) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
+ if (http->status_code / 100 == 2 &&
+ (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->content_length != -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = parse_uint(nv->value->base, nv->value->len);
+ if (http->content_length == -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ /* disallowed header fields */
+ case NGHTTP3_QPACK_TOKEN_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
+ case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
+ case NGHTTP3_QPACK_TOKEN_UPGRADE:
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ default:
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+/* Generated by genauthroitychartbl.py */
+static char VALID_AUTHORITY_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 /* " */, 0 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */,
+ 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 /* ] */, 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 /* } */, 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_authority(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_AUTHORITY_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int check_scheme(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+
+ if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
+ return 0;
+ }
+
+ last = value + len;
+ ++value;
+
+ for (; value != last; ++value) {
+ if (!(('A' <= *value && *value <= 'Z') ||
+ ('a' <= *value && *value <= 'z') ||
+ ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
+ *value == '.')) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type,
+ nghttp3_qpack_nv *nv, int request, int trailers) {
+ int rv;
+ size_t i;
+ uint8_t c;
+
+ if (!nghttp3_check_header_name(nv->name->base, nv->name->len)) {
+ if (nv->name->len > 0 && nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* header field name must be lower-cased without exception */
+ for (i = 0; i < nv->name->len; ++i) {
+ c = nv->name->base[i];
+ if ('A' <= c && c <= 'Z') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+ /* When ignoring regular header fields, we set this flag so that
+ we still enforce header field ordering rule for pseudo header
+ fields. */
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+
+ 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) {
+ rv = check_scheme(nv->value->base, nv->value->len);
+ } else {
+ rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
+ }
+
+ if (rv == 0) {
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* When ignoring regular header fields, we set this flag so that
+ we still enforce header field ordering rule for pseudo header
+ fields. */
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+
+ if (request) {
+ rv = http_request_on_header(http, frame_type, nv, trailers,
+ /* connect_protocol = */ 0);
+ } else {
+ rv = http_response_on_header(http, nv, trailers);
+ }
+
+ if (nv->name->base[0] != ':') {
+ switch (rv) {
+ case 0:
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+int nghttp3_http_on_request_headers(nghttp3_http_state *http) {
+ if (!(http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
+ (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
+ if ((http->flags & (NGHTTP3_HTTP_FLAG__SCHEME | NGHTTP3_HTTP_FLAG__PATH)) ||
+ (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = -1;
+ } else {
+ if ((http->flags & NGHTTP3_HTTP_FLAG_REQ_HEADERS) !=
+ NGHTTP3_HTTP_FLAG_REQ_HEADERS ||
+ (http->flags &
+ (NGHTTP3_HTTP_FLAG__AUTHORITY | NGHTTP3_HTTP_FLAG_HOST)) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if ((http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
+ ((http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) == 0 ||
+ (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (!check_path(http)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_response_headers(nghttp3_http_state *http) {
+ if ((http->flags & NGHTTP3_HTTP_FLAG__STATUS) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+
+ 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->content_length = -1;
+ http->status_code = -1;
+ return 0;
+ }
+
+ http->flags =
+ (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
+
+ if (!expect_response_body(http)) {
+ http->content_length = 0;
+ } else if (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
+ http->content_length = -1;
+ }
+
+ return 0;
+}
+
+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)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n) {
+ stream->rx.http.recv_content_length += (int64_t)n;
+
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
+ (stream->rx.http.content_length != -1 &&
+ stream->rx.http.recv_content_length > stream->rx.http.content_length)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+
+ return 0;
+}
+
+void nghttp3_http_record_request_method(nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen) {
+ size_t i;
+ const nghttp3_nv *nv;
+
+ /* TODO we should do this strictly. */
+ for (i = 0; i < nvlen; ++i) {
+ nv = &nva[i];
+ if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
+ memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
+ continue;
+ }
+ if (lstreq("CONNECT", nv->value, nv->valuelen)) {
+ stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
+ return;
+ }
+ if (lstreq("HEAD", nv->value, nv->valuelen)) {
+ stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
+ return;
+ }
+ return;
+ }
+}
+
+/* Generated by gennmchartbl.py */
+static const int VALID_HD_NAME_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 /* @ */, 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 /* ] */, 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 */
+};
+
+int nghttp3_check_header_name(const uint8_t *name, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+ if (*name == ':') {
+ if (len == 1) {
+ return 0;
+ }
+ ++name;
+ --len;
+ }
+ for (last = name + len; name != last; ++name) {
+ if (!VALID_HD_NAME_CHARS[*name]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genvchartbl.py */
+static const int VALID_HD_VALUE_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 1 /* 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 /* ! */, 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 */
+};
+
+int nghttp3_check_header_value(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_HD_VALUE_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h
new file mode 100644
index 0000000000..65ec91759b
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h
@@ -0,0 +1,146 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2015 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_HTTP_H
+#define NGHTTP3_HTTP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_stream nghttp3_stream;
+
+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
+/* 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
+/* host is not pseudo header, but we require either host or
+ :authority */
+#define NGHTTP3_HTTP_FLAG_HOST 0x10
+#define NGHTTP3_HTTP_FLAG__STATUS 0x20
+/* 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
+/* 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_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
+/* path "*" */
+#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800
+/* scheme */
+/* "http" or "https" scheme */
+#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000
+/* set if final response is expected */
+#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000
+/* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header
+ field is seen. */
+#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000
+
+/*
+ * 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 returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Invalid HTTP header field was received.
+ * NGHTTP3_ERR_REMOVE_HTTP_HEADER
+ * 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);
+
+/*
+ * This function is called when request header is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Required HTTP header field was not received; or an invalid
+ * header field was received.
+ */
+int nghttp3_http_on_request_headers(nghttp3_http_state *http);
+
+/*
+ * This function is called when response header is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Required HTTP header field was not received; or an invalid
+ * header field was received.
+ */
+int nghttp3_http_on_response_headers(nghttp3_http_state *http);
+
+/*
+ * This function is called when read side stream is closed. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING
+ * HTTP messaging is violated.
+ */
+int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream);
+
+/*
+ * This function is called when chunk of data is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING
+ * HTTP messaging is violated.
+ */
+int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n);
+
+/*
+ * This function inspects header fields in |nva| of length |nvlen| and
+ * records its method in stream->http_flags.
+ */
+void nghttp3_http_record_request_method(nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen);
+
+#endif /* NGHTTP3_HTTP_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c
new file mode 100644
index 0000000000..cd8fd82e6b
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c
@@ -0,0 +1,88 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_idtr.h"
+
+#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;
+ }
+
+ idtr->server = server;
+ idtr->mem = mem;
+
+ return 0;
+}
+
+void nghttp3_idtr_free(nghttp3_idtr *idtr) {
+ if (idtr == NULL) {
+ return;
+ }
+
+ nghttp3_gaptr_free(&idtr->gap);
+}
+
+/*
+ * id_from_stream_id translates |stream_id| to id space used by
+ * nghttp3_idtr.
+ */
+static uint64_t id_from_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2);
+}
+
+int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ if (nghttp3_gaptr_is_pushed(&idtr->gap, q, 1)) {
+ return NGHTTP3_ERR_STREAM_IN_USE;
+ }
+
+ return nghttp3_gaptr_push(&idtr->gap, q, 1);
+}
+
+int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ return nghttp3_gaptr_is_pushed(&idtr->gap, q, 1);
+}
+
+uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr) {
+ return nghttp3_gaptr_first_gap_offset(&idtr->gap);
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h
new file mode 100644
index 0000000000..c4d60861ab
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h
@@ -0,0 +1,99 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_IDTR_H
+#define NGHTTP3_IDTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_gaptr.h"
+
+/*
+ * nghttp3_idtr tracks the usage of stream ID.
+ */
+typedef struct nghttp3_idtr {
+ /* gap maintains the range of ID which is not used yet. Initially,
+ its range is [0, UINT64_MAX). */
+ nghttp3_gaptr gap;
+ /* 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.
+ *
+ * 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);
+
+/*
+ * nghttp3_idtr_free frees resources allocated for |idtr|.
+ */
+void nghttp3_idtr_free(nghttp3_idtr *idtr);
+
+/*
+ * nghttp3_idtr_open claims that |stream_id| is in used.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_STREAM_IN_USE
+ * ID has already been used.
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id);
+
+/*
+ * nghttp3_idtr_open tells whether ID |stream_id| is in used or not.
+ *
+ * It returns nonzero if |stream_id| is used.
+ */
+int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id);
+
+/*
+ * nghttp3_idtr_first_gap returns the first id of first gap. If there
+ * is no gap, it returns UINT64_MAX. The returned id is an id space
+ * used in this object internally, and not stream ID.
+ */
+uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr);
+
+#endif /* NGHTTP3_IDTR_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c
new file mode 100644
index 0000000000..0896cb693f
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c
@@ -0,0 +1,749 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_ksl.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_macro.h"
+#include "nghttp3_mem.h"
+#include "nghttp3_range.h"
+
+static size_t ksl_nodelen(size_t keylen) {
+ return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
+ (size_t)~0xf;
+}
+
+static size_t ksl_blklen(size_t nodelen) {
+ return sizeof(nghttp3_ksl_blk) + nodelen * NGHTTP3_KSL_MAX_NBLK -
+ sizeof(uint64_t);
+}
+
+/*
+ * ksl_node_set_key sets |key| to |node|.
+ */
+static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node,
+ const void *key) {
+ memcpy(node->key, key, ksl->keylen);
+}
+
+int 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;
+ ksl->compar = compar;
+ ksl->keylen = keylen;
+ ksl->nodelen = nodelen;
+ ksl->n = 0;
+ ksl->mem = mem;
+
+ head = ksl->head;
+ head->next = head->prev = NULL;
+ head->n = 0;
+ head->leaf = 1;
+
+ return 0;
+}
+
+/*
+ * ksl_free_blk frees |blk| recursively.
+ */
+static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ size_t i;
+
+ if (!blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk);
+ }
+ }
+
+ nghttp3_mem_free(ksl->mem, blk);
+}
+
+void nghttp3_ksl_free(nghttp3_ksl *ksl) {
+ if (!ksl) {
+ return;
+ }
+
+ ksl_free_blk(ksl, ksl->head);
+}
+
+/*
+ * ksl_split_blk splits |blk| into 2 nghttp3_ksl_blk objects. The new
+ * nghttp3_ksl_blk is always the "right" block.
+ *
+ * It returns the pointer to the nghttp3_ksl_blk created which is the
+ * located at the right of |blk|, or NULL which indicates out of
+ * memory error.
+ */
+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));
+ if (rblk == NULL) {
+ return NULL;
+ }
+
+ rblk->next = blk->next;
+ blk->next = rblk;
+ if (rblk->next) {
+ rblk->next->prev = rblk;
+ } else if (ksl->back == blk) {
+ ksl->back = rblk;
+ }
+ rblk->prev = blk;
+ rblk->leaf = blk->leaf;
+
+ rblk->n = blk->n / 2;
+
+ memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n),
+ ksl->nodelen * rblk->n);
+
+ blk->n -= rblk->n;
+
+ assert(blk->n >= NGHTTP3_KSL_MIN_NBLK);
+ assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK);
+
+ return rblk;
+}
+
+/*
+ * ksl_split_node splits a node included in |blk| at the position |i|
+ * into 2 adjacent nodes. The new node is always inserted at the
+ * position |i+1|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *node;
+ nghttp3_ksl_blk *lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk, *rblk;
+
+ rblk = ksl_split_blk(ksl, lblk);
+ if (rblk == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ memmove(blk->nodes + (i + 2) * ksl->nodelen,
+ blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+ node->blk = rblk;
+ ++blk->n;
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+
+ return 0;
+}
+
+/*
+ * ksl_split_head splits a head (root) block. It increases the height
+ * of skip list by 1.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_head(nghttp3_ksl *ksl) {
+ nghttp3_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
+ nghttp3_ksl_node *node;
+
+ rblk = ksl_split_blk(ksl, ksl->head);
+ if (rblk == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ lblk = ksl->head;
+
+ nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+ if (nhead == NULL) {
+ nghttp3_mem_free(ksl->mem, rblk);
+ return NGHTTP3_ERR_NOMEM;
+ }
+ nhead->next = nhead->prev = NULL;
+ nhead->n = 2;
+ nhead->leaf = 0;
+
+ node = nghttp3_ksl_nth_node(ksl, nhead, 0);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ node->blk = lblk;
+
+ node = nghttp3_ksl_nth_node(ksl, nhead, 1);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+ node->blk = rblk;
+
+ ksl->head = nhead;
+
+ return 0;
+}
+
+/*
+ * insert_node inserts a node whose key is |key| with the associated
+ * |data| at the index of |i|. This function assumes that the number
+ * of nodes contained by |blk| is strictly less than
+ * NGHTTP3_KSL_MAX_NBLK.
+ */
+static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i,
+ const nghttp3_ksl_key *key, void *data) {
+ nghttp3_ksl_node *node;
+
+ assert(blk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
+ ksl->nodelen * (blk->n - i));
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, key);
+ node->data = data;
+
+ ++blk->n;
+}
+
+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;
+ 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;
+ }
+ }
+
+ return (size_t)right;
+}
+
+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_node *node;
+ size_t i;
+ int rv;
+
+ if (blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_head(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ blk = ksl->head;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i < blk->n &&
+ !ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ ksl_insert_node(ksl, blk, i, key, data);
+ ++ksl->n;
+ if (it) {
+ nghttp3_ksl_it_init(it, ksl, blk, i);
+ }
+ return 0;
+ }
+
+ if (i == blk->n) {
+ /* This insertion extends the largest key in this subtree. */
+ for (; !blk->leaf;) {
+ node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1);
+ if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, blk->n - 1);
+ if (rv != 0) {
+ return rv;
+ }
+ node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1);
+ }
+ ksl_node_set_key(ksl, node, key);
+ blk = node->blk;
+ }
+ ksl_insert_node(ksl, blk, blk->n, key, data);
+ ++ksl->n;
+ if (it) {
+ nghttp3_ksl_it_init(it, ksl, blk, blk->n - 1);
+ }
+ return 0;
+ }
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, i);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ksl->compar((nghttp3_ksl_key *)node->key, key)) {
+ node = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+ if (ksl->compar((nghttp3_ksl_key *)node->key, key)) {
+ ksl_node_set_key(ksl, node, key);
+ }
+ }
+ }
+
+ blk = node->blk;
+ }
+}
+
+/*
+ * ksl_remove_node removes the node included in |blk| at the index of
+ * |i|.
+ */
+static void ksl_remove_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ --blk->n;
+}
+
+/*
+ * ksl_merge_node merges 2 nodes which are the nodes at the index of
+ * |i| and |i + 1|.
+ *
+ * If |blk| is the direct descendant of head (root) block and the head
+ * block contains just 2 nodes, the merged block becomes head block,
+ * which decreases the height of |ksl| by 1.
+ *
+ * This function returns the pointer to the merged block.
+ */
+static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
+ size_t i) {
+ nghttp3_ksl_blk *lblk, *rblk;
+
+ assert(i + 1 < blk->n);
+
+ lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ rblk = nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk;
+
+ assert(lblk->n + rblk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
+ ksl->nodelen * rblk->n);
+
+ lblk->n += rblk->n;
+ lblk->next = rblk->next;
+ if (lblk->next) {
+ lblk->next->prev = lblk;
+ } else if (ksl->back == rblk) {
+ ksl->back = lblk;
+ }
+
+ nghttp3_mem_free(ksl->mem, rblk);
+
+ if (ksl->head == blk && blk->n == 2) {
+ nghttp3_mem_free(ksl->mem, ksl->head);
+ ksl->head = lblk;
+ } else {
+ ksl_remove_node(ksl, blk, i + 1);
+ ksl_node_set_key(ksl, nghttp3_ksl_nth_node(ksl, blk, i),
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ }
+
+ return lblk;
+}
+
+/*
+ * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i - 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible.
+ */
+static void ksl_shift_left(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i > 0);
+
+ lnode = nghttp3_ksl_nth_node(ksl, blk, i - 1);
+ rnode = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ assert(lnode->blk->n < NGHTTP3_KSL_MAX_NBLK);
+ assert(rnode->blk->n > NGHTTP3_KSL_MIN_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - lnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n);
+ assert(rnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n);
+
+ memcpy(lnode->blk->nodes + ksl->nodelen * lnode->blk->n, rnode->blk->nodes,
+ ksl->nodelen * n);
+
+ lnode->blk->n += (uint32_t)n;
+ rnode->blk->n -= (uint32_t)n;
+
+ ksl_node_set_key(
+ ksl, lnode,
+ nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+
+ memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen * n,
+ ksl->nodelen * rnode->blk->n);
+}
+
+/*
+ * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i + 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible..
+ */
+static void ksl_shift_right(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i < blk->n - 1);
+
+ lnode = nghttp3_ksl_nth_node(ksl, blk, i);
+ rnode = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+
+ assert(lnode->blk->n > NGHTTP3_KSL_MIN_NBLK);
+ assert(rnode->blk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - rnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n);
+ assert(rnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n);
+
+ memmove(rnode->blk->nodes + ksl->nodelen * n, rnode->blk->nodes,
+ ksl->nodelen * rnode->blk->n);
+
+ rnode->blk->n += (uint32_t)n;
+ lnode->blk->n -= (uint32_t)n;
+
+ memcpy(rnode->blk->nodes, lnode->blk->nodes + ksl->nodelen * lnode->blk->n,
+ ksl->nodelen * n);
+
+ ksl_node_set_key(
+ ksl, lnode,
+ nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+}
+
+/*
+ * key_equal returns nonzero if |lhs| and |rhs| are equal using the
+ * function |compar|.
+ */
+static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ return !compar(lhs, rhs) && !compar(rhs, lhs);
+}
+
+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 (!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) {
+ blk = ksl_merge_node(ksl, ksl->head, 0);
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (i == blk->n) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (blk->leaf) {
+ if (ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ ksl_remove_node(ksl, blk, i);
+ --ksl->n;
+ if (it) {
+ if (blk->n == i && blk->next) {
+ nghttp3_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ nghttp3_ksl_it_init(it, ksl, blk, i);
+ }
+ }
+ return 0;
+ }
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ blk = node->blk;
+ continue;
+ }
+
+ assert(node->blk->n == NGHTTP3_KSL_MIN_NBLK);
+
+ if (i + 1 < blk->n &&
+ nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ ksl_shift_left(ksl, blk, i + 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i > 0 &&
+ nghttp3_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ ksl_shift_right(ksl, blk, i - 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i + 1 < blk->n) {
+ blk = ksl_merge_node(ksl, blk, i);
+ continue;
+ }
+
+ assert(i > 0);
+
+ blk = ksl_merge_node(ksl, blk, i - 1);
+ }
+}
+
+nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_it it;
+ size_t i;
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_it it;
+ size_t i;
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
+ const nghttp3_ksl_key *new_key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_node *node;
+ size_t i;
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
+
+ assert(i < blk->n);
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (blk->leaf) {
+ assert(key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key));
+ ksl_node_set_key(ksl, node, new_key);
+ return;
+ }
+
+ if (key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key) ||
+ ksl->compar((nghttp3_ksl_key *)node->key, new_key)) {
+ ksl_node_set_key(ksl, node, new_key);
+ }
+
+ blk = node->blk;
+ }
+}
+
+static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) {
+ size_t i;
+ nghttp3_ksl_node *node;
+
+ fprintf(stderr, "LV=%zu n=%u\n", level, blk->n);
+
+ if (blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key);
+ }
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ for (i = 0; i < blk->n; ++i) {
+ ksl_print(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk, level + 1);
+ }
+}
+
+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);
+ }
+ }
+
+ ksl->front = ksl->back = ksl->head;
+ ksl->n = 0;
+
+ head = ksl->head;
+
+ head->next = head->prev = NULL;
+ head->n = 0;
+ head->leaf = 1;
+}
+
+void nghttp3_ksl_print(nghttp3_ksl *ksl) { 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);
+ 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);
+ return it;
+}
+
+void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
+ nghttp3_ksl_blk *blk, size_t i) {
+ it->ksl = ksl;
+ it->blk = blk;
+ 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));
+
+ if (it->i == 0) {
+ it->blk = it->blk->prev;
+ it->i = it->blk->n - 1;
+ } else {
+ --it->i;
+ }
+}
+
+int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) {
+ return it->i == 0 && it->blk->prev == NULL;
+}
+
+int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_range *a = lhs, *b = rhs;
+ return a->begin < b->begin;
+}
+
+int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_range *a = lhs, *b = rhs;
+ return a->begin < b->begin &&
+ !(nghttp3_max(a->begin, b->begin) < nghttp3_min(a->end, b->end));
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h
new file mode 100644
index 0000000000..7ba36bb9cb
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h
@@ -0,0 +1,331 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_KSL_H
+#define NGHTTP3_KSL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <nghttp3/nghttp3.h>
+
+/*
+ * Skip List using single key instead of range.
+ */
+
+#define NGHTTP3_KSL_DEGR 16
+/* NGHTTP3_KSL_MAX_NBLK is the maximum number of nodes which a single
+ block can contain. */
+#define NGHTTP3_KSL_MAX_NBLK (2 * NGHTTP3_KSL_DEGR - 1)
+/* NGHTTP3_KSL_MIN_NBLK is the minimum number of nodes which a single
+ block other than root must contains. */
+#define NGHTTP3_KSL_MIN_NBLK (NGHTTP3_KSL_DEGR - 1)
+
+/*
+ * nghttp3_ksl_key represents key in nghttp3_ksl.
+ */
+typedef void nghttp3_ksl_key;
+
+typedef struct nghttp3_ksl_node nghttp3_ksl_node;
+
+typedef struct nghttp3_ksl_blk nghttp3_ksl_blk;
+
+/*
+ * nghttp3_ksl_node is a node which contains either nghttp3_ksl_blk or
+ * opaque data. If a node is an internal node, it contains
+ * nghttp3_ksl_blk. Otherwise, it has data. The key is stored at the
+ * location starting at key.
+ */
+struct nghttp3_ksl_node {
+ union {
+ nghttp3_ksl_blk *blk;
+ void *data;
+ };
+ union {
+ uint64_t align;
+ /* key is a buffer to include key associated to this node.
+ Because the length of key is unknown until nghttp3_ksl_init is
+ called, the actual buffer will be allocated after this
+ field. */
+ uint8_t key[1];
+ };
+};
+
+/*
+ * 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];
+ };
+};
+
+/*
+ * nghttp3_ksl_compar is a function type which returns nonzero if key
+ * |lhs| should be placed before |rhs|. It returns 0 otherwise.
+ */
+typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+typedef struct nghttp3_ksl nghttp3_ksl;
+
+typedef struct nghttp3_ksl_it nghttp3_ksl_it;
+
+/*
+ * nghttp3_ksl_it is a forward iterator to iterate nodes.
+ */
+struct nghttp3_ksl_it {
+ const nghttp3_ksl *ksl;
+ nghttp3_ksl_blk *blk;
+ size_t i;
+};
+
+/*
+ * nghttp3_ksl is a deterministic paged skip list.
+ */
+struct nghttp3_ksl {
+ /* head points to the root block. */
+ nghttp3_ksl_blk *head;
+ /* front points to the first leaf block. */
+ nghttp3_ksl_blk *front;
+ /* back points to the last leaf block. */
+ nghttp3_ksl_blk *back;
+ nghttp3_ksl_compar compar;
+ size_t n;
+ /* keylen is the size of key */
+ size_t keylen;
+ /* 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);
+
+/*
+ * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is
+ * NULL, this function does nothing. It does not free the memory
+ * region pointed by |ksl| itself.
+ */
+void nghttp3_ksl_free(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_insert inserts |key| with its associated |data|. On
+ * successful insertion, the iterator points to the inserted node is
+ * stored in |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * |key| already exists.
+ */
+int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key, void *data);
+
+/*
+ * nghttp3_ksl_remove removes the |key| from |ksl|.
+ *
+ * This function assigns the iterator to |*it|, which points to the
+ * node which is located at the right next of the removed node if |it|
+ * is not NULL. If |key| is not found, no deletion takes place and
+ * the return value of nghttp3_ksl_end(ksl) is assigned to |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * |key| does not exist.
+ */
+int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ 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
+ * node, it returns the iterator which satisfies nghttp3_ksl_it_end(it)
+ * != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key);
+
+/*
+ * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound,
+ * but it takes custom function |compar| to do lower bound search.
+ */
+nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar);
+
+/*
+ * nghttp3_ksl_update_key replaces the key of nodes which has |old_key|
+ * with |new_key|. |new_key| must be strictly greater than the
+ * previous node and strictly smaller than the next node.
+ */
+void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
+ const nghttp3_ksl_key *new_key);
+
+/*
+ * nghttp3_ksl_begin returns the iterator which points to the first
+ * node. If there is no node in |ksl|, it returns the iterator which
+ * satisfies nghttp3_ksl_it_end(it) != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_end returns the iterator which points to the node
+ * following the last node. The returned object satisfies
+ * nghttp3_ksl_it_end(). If there is no node in |ksl|, it returns the
+ * iterator which satisfies nghttp3_ksl_it_begin(it) != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_len returns the number of elements stored in |ksl|.
+ */
+size_t nghttp3_ksl_len(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_clear removes all elements stored in |ksl|.
+ */
+void nghttp3_ksl_clear(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_nth_node returns the |n|th node under |blk|.
+ */
+#define nghttp3_ksl_nth_node(KSL, BLK, N) \
+ ((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
+
+/*
+ * nghttp3_ksl_print prints its internal state in stderr. It assumes
+ * that the key is of type int64_t. This function should be used for
+ * the debugging purpose only.
+ */
+void nghttp3_ksl_print(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_it_init initializes |it|.
+ */
+void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
+ nghttp3_ksl_blk *blk, size_t i);
+
+/*
+ * nghttp3_ksl_it_get returns the data associated to the node which
+ * |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);
+
+/*
+ * nghttp3_ksl_it_next advances the iterator by one. It is undefined
+ * if this function is called when nghttp3_ksl_it_end(it) returns
+ * nonzero.
+ */
+#define nghttp3_ksl_it_next(IT) \
+ (++(IT)->i == (IT)->blk->n && (IT)->blk->next \
+ ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
+ : 0)
+
+/*
+ * nghttp3_ksl_it_prev moves backward the iterator by one. It is
+ * undefined if this function is called when nghttp3_ksl_it_begin(it)
+ * returns nonzero.
+ */
+void nghttp3_ksl_it_prev(nghttp3_ksl_it *it);
+
+/*
+ * nghttp3_ksl_it_end returns nonzero if |it| points to the beyond the
+ * last node.
+ */
+#define nghttp3_ksl_it_end(IT) \
+ ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
+
+/*
+ * nghttp3_ksl_it_begin returns nonzero if |it| points to the first
+ * node. |it| might satisfy both nghttp3_ksl_it_begin(&it) and
+ * nghttp3_ksl_it_end(&it) if the skip list has no node.
+ */
+int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it);
+
+/*
+ * nghttp3_ksl_key returns the key of the node which |it| points to.
+ * It is undefined to call this function when nghttp3_ksl_it_end(it)
+ * returns nonzero.
+ */
+#define nghttp3_ksl_it_key(IT) \
+ ((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
+
+/*
+ * nghttp3_ksl_range_compar is an implementation of
+ * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * nghttp3_range object and the function returns nonzero if (const
+ * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
+ * *)(rhs->ptr)->begin.
+ */
+int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+/*
+ * nghttp3_ksl_range_exclusive_compar is an implementation of
+ * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * nghttp3_range object and the function returns nonzero if (const
+ * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
+ * *)(rhs->ptr)->begin and the 2 ranges do not intersect.
+ */
+int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+#endif /* NGHTTP3_KSL_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h
new file mode 100644
index 0000000000..6ee704cc47
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h
@@ -0,0 +1,47 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MACRO_H
+#define NGHTTP3_MACRO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stddef.h>
+
+#include <nghttp3/nghttp3.h>
+
+#define nghttp3_min(A, B) ((A) < (B) ? (A) : (B))
+#define nghttp3_max(A, B) ((A) > (B) ? (A) : (B))
+
+#define nghttp3_struct_of(ptr, type, member) \
+ ((type *)(void *)((char *)(ptr)-offsetof(type, member)))
+
+#define nghttp3_arraylen(A) (sizeof(A) / sizeof(*(A)))
+
+#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
+
+#endif /* NGHTTP3_MACRO_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c
new file mode 100644
index 0000000000..a80955ad7a
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c
@@ -0,0 +1,336 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_map.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_conv.h"
+
+#define INITIAL_TABLE_LENGTH 256
+
+int 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->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 *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);
+ 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;
+ }
+ }
+}
+
+int nghttp3_map_each(nghttp3_map *map,
+ int (*func)(nghttp3_map_entry *entry, void *ptr),
+ void *ptr) {
+ int rv;
+ 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) {
+ rv = func(bkt->ptr, ptr);
+ if (rv != 0) {
+ return rv;
+ }
+ assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0);
+ 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;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) {
+ entry->key = key;
+}
+
+/* FNV1a hash */
+static uint32_t hash(key_type key, uint32_t mod) {
+ uint8_t *p, *end;
+ uint32_t h = 0x811C9DC5u;
+
+ key = nghttp3_htonl64(key);
+ p = (uint8_t *)&key;
+ end = p + sizeof(key_type);
+
+ for (; p != end;) {
+ h ^= *p++;
+ h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
+ }
+
+ return h & (mod - 1);
+}
+
+static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) {
+ return *(key_type *)lhs < *(key_type *)rhs;
+}
+
+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;
+
+ if (bkt->ptr == NULL &&
+ (bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) {
+ bkt->ptr = entry;
+ return 0;
+ }
+
+ if (!bkt->ksl) {
+ bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl));
+ if (bkt->ksl == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+ nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
+ }
+
+ if (bkt->ptr) {
+ rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
+ if (rv != 0) {
+ return rv;
+ }
+
+ bkt->ptr = NULL;
+ }
+
+ return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
+}
+
+/* new_tablelen must be power of 2 */
+static int map_resize(nghttp3_map *map, uint32_t new_tablelen) {
+ uint32_t i;
+ nghttp3_map_bucket *new_table;
+ nghttp3_map_bucket *bkt;
+ nghttp3_ksl_it it;
+ int rv;
+
+ new_table =
+ nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket));
+ if (new_table == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ 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);
+ continue;
+ }
+
+ 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);
+ }
+ }
+
+ nghttp3_mem_free(map->mem, map->table);
+ map->tablelen = new_tablelen;
+ 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 rv;
+
+ /* 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;
+ }
+ }
+ rv = map_insert(map, map->table, map->tablelen, new_entry);
+ if (rv != 0) {
+ return rv;
+ }
+ ++map->size;
+ 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;
+
+ if (bkt->ptr) {
+ if (bkt->ptr->key == key) {
+ return bkt->ptr;
+ }
+ 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) {
+ return NULL;
+ }
+ return nghttp3_ksl_it_get(&it);
+ }
+
+ return NULL;
+}
+
+int nghttp3_map_remove(nghttp3_map *map, key_type key) {
+ nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
+ int rv;
+
+ if (bkt->ptr) {
+ if (bkt->ptr->key == key) {
+ bkt->ptr = NULL;
+ --map->size;
+ return 0;
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (bkt->ksl) {
+ rv = nghttp3_ksl_remove(bkt->ksl, NULL, &key);
+ if (rv != 0) {
+ return rv;
+ }
+ --map->size;
+ return 0;
+ }
+
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+}
+
+void nghttp3_map_clear(nghttp3_map *map) {
+ uint32_t i;
+ nghttp3_map_bucket *bkt;
+
+ 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;
+ }
+ }
+
+ map->size = 0;
+}
+
+size_t nghttp3_map_size(nghttp3_map *map) { return map->size; }
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h
new file mode 100644
index 0000000000..2eae2e7cb7
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h
@@ -0,0 +1,154 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MAP_H
+#define NGHTTP3_MAP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#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 struct nghttp3_map_bucket {
+ nghttp3_map_entry *ptr;
+ nghttp3_ksl *ksl;
+} nghttp3_map_bucket;
+
+typedef struct nghttp3_map {
+ nghttp3_map_bucket *table;
+ const nghttp3_mem *mem;
+ size_t size;
+ uint32_t tablelen;
+} 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);
+
+/*
+ * Deallocates any resources allocated for |map|. The stored entries
+ * are not freed by this function. Use nghttp3_map_each_free() to free
+ * each entries.
+ */
+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
+ * 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 *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|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * The item associated by |key| already exists.
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory
+ */
+int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry);
+
+/*
+ * Returns the entry associated by the key |key|. If there is no such
+ * entry, this function returns NULL.
+ */
+nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key);
+
+/*
+ * Removes the entry associated by the key |key| from the |map|. The
+ * removed entry 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.
+ */
+int nghttp3_map_remove(nghttp3_map *map, key_type key);
+
+/*
+ * Removes all entries from |map|.
+ */
+void nghttp3_map_clear(nghttp3_map *map);
+
+/*
+ * Returns the number of items stored in the map |map|.
+ */
+size_t nghttp3_map_size(nghttp3_map *map);
+
+/*
+ * Applies the function |func| to each entry 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
+ * |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
+ * nghttp3_map_each_free() instead.
+ */
+int nghttp3_map_each(nghttp3_map *map,
+ int (*func)(nghttp3_map_entry *entry, void *ptr),
+ void *ptr);
+
+#endif /* NGHTTP3_MAP_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c
new file mode 100644
index 0000000000..e5f93f10bd
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c
@@ -0,0 +1,77 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_mem.h"
+
+static void *default_malloc(size_t size, void *mem_user_data) {
+ (void)mem_user_data;
+
+ return malloc(size);
+}
+
+static void default_free(void *ptr, void *mem_user_data) {
+ (void)mem_user_data;
+
+ free(ptr);
+}
+
+static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
+ (void)mem_user_data;
+
+ return calloc(nmemb, size);
+}
+
+static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
+ (void)mem_user_data;
+
+ return realloc(ptr, size);
+}
+
+static nghttp3_mem mem_default = {NULL, default_malloc, default_free,
+ default_calloc, default_realloc};
+
+const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; }
+
+void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) {
+ return mem->malloc(size, mem->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);
+}
+
+void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) {
+ return mem->calloc(nmemb, size, mem->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);
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h
new file mode 100644
index 0000000000..55ef86b4f9
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h
@@ -0,0 +1,45 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MEM_H
+#define NGHTTP3_MEM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/* Convenient wrapper functions to call allocator function in
+ |mem|. */
+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);
+
+#endif /* NGHTTP3_MEM_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c
new file mode 100644
index 0000000000..5d09050ae6
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c
@@ -0,0 +1,168 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_pq.h"
+
+#include <assert.h>
+
+#include "nghttp3_macro.h"
+
+void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less,
+ const nghttp3_mem *mem) {
+ pq->mem = mem;
+ pq->capacity = 0;
+ pq->q = NULL;
+ pq->length = 0;
+ pq->less = less;
+}
+
+void nghttp3_pq_free(nghttp3_pq *pq) {
+ nghttp3_mem_free(pq->mem, pq->q);
+ pq->q = NULL;
+}
+
+static void swap(nghttp3_pq *pq, size_t i, size_t j) {
+ nghttp3_pq_entry *a = pq->q[i];
+ nghttp3_pq_entry *b = pq->q[j];
+
+ pq->q[i] = b;
+ b->index = i;
+ pq->q[j] = a;
+ a->index = j;
+}
+
+static void bubble_up(nghttp3_pq *pq, size_t index) {
+ size_t parent;
+ while (index != 0) {
+ parent = (index - 1) / 2;
+ if (!pq->less(pq->q[index], pq->q[parent])) {
+ return;
+ }
+ swap(pq, parent, index);
+ index = parent;
+ }
+}
+
+int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item) {
+ if (pq->capacity <= pq->length) {
+ void *nq;
+ size_t ncapacity;
+
+ ncapacity = nghttp3_max(4, (pq->capacity * 2));
+
+ nq = nghttp3_mem_realloc(pq->mem, pq->q,
+ ncapacity * sizeof(nghttp3_pq_entry *));
+ if (nq == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+ pq->capacity = ncapacity;
+ pq->q = nq;
+ }
+ pq->q[pq->length] = item;
+ item->index = pq->length;
+ ++pq->length;
+ bubble_up(pq, pq->length - 1);
+ return 0;
+}
+
+nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq) {
+ assert(pq->length);
+ return pq->q[0];
+}
+
+static void bubble_down(nghttp3_pq *pq, size_t index) {
+ size_t i, j, minindex;
+ for (;;) {
+ j = index * 2 + 1;
+ minindex = index;
+ for (i = 0; i < 2; ++i, ++j) {
+ if (j >= pq->length) {
+ break;
+ }
+ if (pq->less(pq->q[j], pq->q[minindex])) {
+ minindex = j;
+ }
+ }
+ if (minindex == index) {
+ return;
+ }
+ swap(pq, index, minindex);
+ index = minindex;
+ }
+}
+
+void nghttp3_pq_pop(nghttp3_pq *pq) {
+ if (pq->length > 0) {
+ pq->q[0] = pq->q[pq->length - 1];
+ pq->q[0]->index = 0;
+ --pq->length;
+ bubble_down(pq, 0);
+ }
+}
+
+void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item) {
+ assert(pq->q[item->index] == item);
+
+ if (item->index == 0) {
+ nghttp3_pq_pop(pq);
+ return;
+ }
+
+ if (item->index == pq->length - 1) {
+ --pq->length;
+ return;
+ }
+
+ pq->q[item->index] = pq->q[pq->length - 1];
+ pq->q[item->index]->index = item->index;
+ --pq->length;
+
+ if (pq->less(item, pq->q[item->index])) {
+ bubble_down(pq, item->index);
+ } else {
+ bubble_up(pq, item->index);
+ }
+}
+
+int nghttp3_pq_empty(const nghttp3_pq *pq) { return pq->length == 0; }
+
+size_t nghttp3_pq_size(const nghttp3_pq *pq) { return pq->length; }
+
+int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg) {
+ size_t i;
+
+ if (pq->length == 0) {
+ return 0;
+ }
+ for (i = 0; i < pq->length; ++i) {
+ if ((*fun)(pq->q[i], arg)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void nghttp3_pq_clear(nghttp3_pq *pq) { pq->length = 0; }
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h
new file mode 100644
index 0000000000..f1f369231d
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h
@@ -0,0 +1,129 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_PQ_H
+#define NGHTTP3_PQ_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+/* Implementation of priority queue */
+
+/* NGHTTP3_PQ_BAD_INDEX is the priority queue index which indicates
+ that an entry is not queued. Assigning this value to
+ nghttp3_pq_entry.index can check that the entry is queued or not. */
+#define NGHTTP3_PQ_BAD_INDEX SIZE_MAX
+
+typedef struct nghttp3_pq_entry {
+ size_t index;
+} nghttp3_pq_entry;
+
+/* "less" function, return nonzero if |lhs| is less than |rhs|. */
+typedef int (*nghttp3_less)(const nghttp3_pq_entry *lhs,
+ const nghttp3_pq_entry *rhs);
+
+typedef struct nghttp3_pq {
+ /* The pointer to the pointer to the item stored */
+ nghttp3_pq_entry **q;
+ /* Memory allocator */
+ const nghttp3_mem *mem;
+ /* The number of items stored */
+ size_t length;
+ /* The maximum number of items this pq can store. This is
+ automatically extended when length is reached to this value. */
+ size_t capacity;
+ /* The less function between items */
+ nghttp3_less less;
+} nghttp3_pq;
+
+/*
+ * Initializes priority queue |pq| with compare function |cmp|.
+ */
+void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, const nghttp3_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |pq|. The stored items are
+ * not freed by this function.
+ */
+void nghttp3_pq_free(nghttp3_pq *pq);
+
+/*
+ * Adds |item| to the priority queue |pq|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item);
+
+/*
+ * Returns item at the top of the queue |pq|. It is undefined if the
+ * queue is empty.
+ */
+nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq);
+
+/*
+ * Pops item at the top of the queue |pq|. The popped item is not
+ * freed by this function.
+ */
+void nghttp3_pq_pop(nghttp3_pq *pq);
+
+/*
+ * Returns nonzero if the queue |pq| is empty.
+ */
+int nghttp3_pq_empty(const nghttp3_pq *pq);
+
+/*
+ * Returns the number of items in the queue |pq|.
+ */
+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
+ * 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.
+ * Otherwise this function returns 0.
+ */
+int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg);
+
+/*
+ * Removes |item| from priority queue.
+ */
+void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item);
+
+void nghttp3_pq_clear(nghttp3_pq *pq);
+
+#endif /* NGHTTP3_PQ_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c
new file mode 100644
index 0000000000..6837942687
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c
@@ -0,0 +1,4095 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_str.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_debug.h"
+
+/* NGHTTP3_QPACK_MAX_QPACK_STREAMS is the maximum number of concurrent
+ nghttp3_qpack_stream object to handle a client which never cancel
+ or acknowledge header block. After this limit, encoder stops using
+ dynamic table. */
+#define NGHTTP3_QPACK_MAX_QPACK_STREAMS 2000
+
+/* Make scalar initialization form of nghttp3_qpack_static_entry */
+#define MAKE_STATIC_ENT(I, T, H) \
+ { I, T, H }
+
+/* Generated by mkstatichdtbl.py */
+static nghttp3_qpack_static_entry token_stable[] = {
+ MAKE_STATIC_ENT(0, NGHTTP3_QPACK_TOKEN__AUTHORITY, 3153725150u),
+ MAKE_STATIC_ENT(15, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(16, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(17, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(18, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(19, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(20, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(21, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(1, NGHTTP3_QPACK_TOKEN__PATH, 3292848686u),
+ MAKE_STATIC_ENT(22, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u),
+ MAKE_STATIC_ENT(23, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u),
+ MAKE_STATIC_ENT(24, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(25, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(26, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(27, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(28, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(63, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(64, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(65, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(66, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(67, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(68, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(69, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(70, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(71, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(29, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u),
+ MAKE_STATIC_ENT(30, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u),
+ MAKE_STATIC_ENT(31, NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING, 3379649177u),
+ MAKE_STATIC_ENT(72, NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE, 1979086614u),
+ MAKE_STATIC_ENT(32, NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES, 1713753958u),
+ MAKE_STATIC_ENT(73, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ 901040780u),
+ MAKE_STATIC_ENT(74, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ 901040780u),
+ MAKE_STATIC_ENT(33, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(34, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(75, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(76, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(77, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(78, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(35, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN,
+ 2710797292u),
+ MAKE_STATIC_ENT(79, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS,
+ 2449824425u),
+ MAKE_STATIC_ENT(80, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS,
+ 3599549072u),
+ MAKE_STATIC_ENT(81, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD,
+ 2417078055u),
+ MAKE_STATIC_ENT(82, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD,
+ 2417078055u),
+ MAKE_STATIC_ENT(2, NGHTTP3_QPACK_TOKEN_AGE, 742476188u),
+ MAKE_STATIC_ENT(83, NGHTTP3_QPACK_TOKEN_ALT_SVC, 2148877059u),
+ MAKE_STATIC_ENT(84, NGHTTP3_QPACK_TOKEN_AUTHORIZATION, 2436257726u),
+ MAKE_STATIC_ENT(36, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(37, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(38, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(39, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(40, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(41, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(3, NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION, 3889184348u),
+ MAKE_STATIC_ENT(42, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u),
+ MAKE_STATIC_ENT(43, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u),
+ MAKE_STATIC_ENT(4, NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH, 1308181789u),
+ MAKE_STATIC_ENT(85, NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY,
+ 1569039836u),
+ MAKE_STATIC_ENT(44, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(45, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(46, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(47, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(48, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(49, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(50, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(51, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(52, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(53, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(54, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(5, NGHTTP3_QPACK_TOKEN_COOKIE, 2007449791u),
+ MAKE_STATIC_ENT(6, NGHTTP3_QPACK_TOKEN_DATE, 3564297305u),
+ MAKE_STATIC_ENT(86, NGHTTP3_QPACK_TOKEN_EARLY_DATA, 4080895051u),
+ MAKE_STATIC_ENT(7, NGHTTP3_QPACK_TOKEN_ETAG, 113792960u),
+ MAKE_STATIC_ENT(87, NGHTTP3_QPACK_TOKEN_EXPECT_CT, 1183214960u),
+ MAKE_STATIC_ENT(88, NGHTTP3_QPACK_TOKEN_FORWARDED, 1485178027u),
+ MAKE_STATIC_ENT(8, NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE, 2213050793u),
+ MAKE_STATIC_ENT(9, NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH, 2536202615u),
+ MAKE_STATIC_ENT(89, NGHTTP3_QPACK_TOKEN_IF_RANGE, 2340978238u),
+ MAKE_STATIC_ENT(10, NGHTTP3_QPACK_TOKEN_LAST_MODIFIED, 3226950251u),
+ MAKE_STATIC_ENT(11, NGHTTP3_QPACK_TOKEN_LINK, 232457833u),
+ MAKE_STATIC_ENT(12, NGHTTP3_QPACK_TOKEN_LOCATION, 200649126u),
+ MAKE_STATIC_ENT(90, NGHTTP3_QPACK_TOKEN_ORIGIN, 3649018447u),
+ MAKE_STATIC_ENT(91, NGHTTP3_QPACK_TOKEN_PURPOSE, 4212263681u),
+ MAKE_STATIC_ENT(55, NGHTTP3_QPACK_TOKEN_RANGE, 4208725202u),
+ MAKE_STATIC_ENT(13, NGHTTP3_QPACK_TOKEN_REFERER, 3969579366u),
+ MAKE_STATIC_ENT(92, NGHTTP3_QPACK_TOKEN_SERVER, 1085029842u),
+ MAKE_STATIC_ENT(14, NGHTTP3_QPACK_TOKEN_SET_COOKIE, 1848371000u),
+ MAKE_STATIC_ENT(56, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(57, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(58, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(93, NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN, 2432297564u),
+ MAKE_STATIC_ENT(94, NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS,
+ 2479169413u),
+ MAKE_STATIC_ENT(95, NGHTTP3_QPACK_TOKEN_USER_AGENT, 606444526u),
+ MAKE_STATIC_ENT(59, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u),
+ MAKE_STATIC_ENT(60, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u),
+ MAKE_STATIC_ENT(61, NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS,
+ 3644557769u),
+ MAKE_STATIC_ENT(96, NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR, 2914187656u),
+ MAKE_STATIC_ENT(97, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u),
+ MAKE_STATIC_ENT(98, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u),
+ MAKE_STATIC_ENT(62, NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION, 2501058888u),
+};
+
+/* 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 \
+ }
+
+static nghttp3_qpack_static_header stable[] = {
+ MAKE_STATIC_HD(":authority", "", NGHTTP3_QPACK_TOKEN__AUTHORITY),
+ MAKE_STATIC_HD(":path", "/", NGHTTP3_QPACK_TOKEN__PATH),
+ MAKE_STATIC_HD("age", "0", NGHTTP3_QPACK_TOKEN_AGE),
+ MAKE_STATIC_HD("content-disposition", "",
+ NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION),
+ MAKE_STATIC_HD("content-length", "0", NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH),
+ MAKE_STATIC_HD("cookie", "", NGHTTP3_QPACK_TOKEN_COOKIE),
+ MAKE_STATIC_HD("date", "", NGHTTP3_QPACK_TOKEN_DATE),
+ MAKE_STATIC_HD("etag", "", NGHTTP3_QPACK_TOKEN_ETAG),
+ MAKE_STATIC_HD("if-modified-since", "",
+ NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE),
+ MAKE_STATIC_HD("if-none-match", "", NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH),
+ MAKE_STATIC_HD("last-modified", "", NGHTTP3_QPACK_TOKEN_LAST_MODIFIED),
+ MAKE_STATIC_HD("link", "", NGHTTP3_QPACK_TOKEN_LINK),
+ MAKE_STATIC_HD("location", "", NGHTTP3_QPACK_TOKEN_LOCATION),
+ MAKE_STATIC_HD("referer", "", NGHTTP3_QPACK_TOKEN_REFERER),
+ MAKE_STATIC_HD("set-cookie", "", NGHTTP3_QPACK_TOKEN_SET_COOKIE),
+ MAKE_STATIC_HD(":method", "CONNECT", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "DELETE", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "GET", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "HEAD", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "OPTIONS", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "POST", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "PUT", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":scheme", "http", NGHTTP3_QPACK_TOKEN__SCHEME),
+ MAKE_STATIC_HD(":scheme", "https", NGHTTP3_QPACK_TOKEN__SCHEME),
+ MAKE_STATIC_HD(":status", "103", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "200", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "304", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "404", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "503", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD("accept", "*/*", NGHTTP3_QPACK_TOKEN_ACCEPT),
+ MAKE_STATIC_HD("accept", "application/dns-message",
+ NGHTTP3_QPACK_TOKEN_ACCEPT),
+ MAKE_STATIC_HD("accept-encoding", "gzip, deflate, br",
+ NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING),
+ MAKE_STATIC_HD("accept-ranges", "bytes", NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES),
+ MAKE_STATIC_HD("access-control-allow-headers", "cache-control",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-headers", "content-type",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-origin", "*",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN),
+ MAKE_STATIC_HD("cache-control", "max-age=0",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "max-age=2592000",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "max-age=604800",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "no-cache",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "no-store",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "public, max-age=31536000",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("content-encoding", "br",
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING),
+ MAKE_STATIC_HD("content-encoding", "gzip",
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING),
+ MAKE_STATIC_HD("content-type", "application/dns-message",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/javascript",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/json",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/x-www-form-urlencoded",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/gif",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/jpeg",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/png",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/css",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/html; charset=utf-8",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/plain",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/plain;charset=utf-8",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("range", "bytes=0-", NGHTTP3_QPACK_TOKEN_RANGE),
+ MAKE_STATIC_HD("strict-transport-security", "max-age=31536000",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("strict-transport-security",
+ "max-age=31536000; includesubdomains",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("strict-transport-security",
+ "max-age=31536000; includesubdomains; preload",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("vary", "accept-encoding", NGHTTP3_QPACK_TOKEN_VARY),
+ MAKE_STATIC_HD("vary", "origin", NGHTTP3_QPACK_TOKEN_VARY),
+ MAKE_STATIC_HD("x-content-type-options", "nosniff",
+ NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS),
+ MAKE_STATIC_HD("x-xss-protection", "1; mode=block",
+ NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION),
+ MAKE_STATIC_HD(":status", "100", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "204", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "206", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "302", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "400", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "403", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "421", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "425", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "500", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD("accept-language", "", NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE),
+ MAKE_STATIC_HD("access-control-allow-credentials", "FALSE",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS),
+ MAKE_STATIC_HD("access-control-allow-credentials", "TRUE",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS),
+ MAKE_STATIC_HD("access-control-allow-headers", "*",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-methods", "get",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-allow-methods", "get, post, options",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-allow-methods", "options",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-expose-headers", "content-length",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS),
+ MAKE_STATIC_HD("access-control-request-headers", "content-type",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS),
+ MAKE_STATIC_HD("access-control-request-method", "get",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD),
+ MAKE_STATIC_HD("access-control-request-method", "post",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD),
+ MAKE_STATIC_HD("alt-svc", "clear", NGHTTP3_QPACK_TOKEN_ALT_SVC),
+ MAKE_STATIC_HD("authorization", "", NGHTTP3_QPACK_TOKEN_AUTHORIZATION),
+ MAKE_STATIC_HD("content-security-policy",
+ "script-src 'none'; object-src 'none'; base-uri 'none'",
+ NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY),
+ MAKE_STATIC_HD("early-data", "1", NGHTTP3_QPACK_TOKEN_EARLY_DATA),
+ MAKE_STATIC_HD("expect-ct", "", NGHTTP3_QPACK_TOKEN_EXPECT_CT),
+ MAKE_STATIC_HD("forwarded", "", NGHTTP3_QPACK_TOKEN_FORWARDED),
+ MAKE_STATIC_HD("if-range", "", NGHTTP3_QPACK_TOKEN_IF_RANGE),
+ MAKE_STATIC_HD("origin", "", NGHTTP3_QPACK_TOKEN_ORIGIN),
+ MAKE_STATIC_HD("purpose", "prefetch", NGHTTP3_QPACK_TOKEN_PURPOSE),
+ MAKE_STATIC_HD("server", "", NGHTTP3_QPACK_TOKEN_SERVER),
+ MAKE_STATIC_HD("timing-allow-origin", "*",
+ NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN),
+ MAKE_STATIC_HD("upgrade-insecure-requests", "1",
+ NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS),
+ MAKE_STATIC_HD("user-agent", "", NGHTTP3_QPACK_TOKEN_USER_AGENT),
+ MAKE_STATIC_HD("x-forwarded-for", "", NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR),
+ MAKE_STATIC_HD("x-frame-options", "deny",
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS),
+ MAKE_STATIC_HD("x-frame-options", "sameorigin",
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS),
+};
+
+static int memeq(const void *s1, const void *s2, size_t n) {
+ return n == 0 || memcmp(s1, s2, n) == 0;
+}
+
+/* Generated by genlibtokenlookup.py */
+static int32_t qpack_lookup_token(const uint8_t *name, size_t namelen) {
+ switch (namelen) {
+ case 2:
+ switch (name[1]) {
+ case 'e':
+ if (memeq("t", name, 1)) {
+ return NGHTTP3_QPACK_TOKEN_TE;
+ }
+ break;
+ }
+ break;
+ case 3:
+ switch (name[2]) {
+ case 'e':
+ if (memeq("ag", name, 2)) {
+ return NGHTTP3_QPACK_TOKEN_AGE;
+ }
+ break;
+ }
+ break;
+ case 4:
+ switch (name[3]) {
+ case 'e':
+ if (memeq("dat", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_DATE;
+ }
+ break;
+ case 'g':
+ if (memeq("eta", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_ETAG;
+ }
+ break;
+ case 'k':
+ if (memeq("lin", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_LINK;
+ }
+ break;
+ case 't':
+ if (memeq("hos", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_HOST;
+ }
+ break;
+ case 'y':
+ if (memeq("var", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_VARY;
+ }
+ break;
+ }
+ break;
+ case 5:
+ switch (name[4]) {
+ case 'e':
+ if (memeq("rang", name, 4)) {
+ return NGHTTP3_QPACK_TOKEN_RANGE;
+ }
+ break;
+ case 'h':
+ if (memeq(":pat", name, 4)) {
+ return NGHTTP3_QPACK_TOKEN__PATH;
+ }
+ break;
+ }
+ break;
+ case 6:
+ switch (name[5]) {
+ case 'e':
+ if (memeq("cooki", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_COOKIE;
+ }
+ break;
+ case 'n':
+ if (memeq("origi", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_ORIGIN;
+ }
+ break;
+ case 'r':
+ if (memeq("serve", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_SERVER;
+ }
+ break;
+ case 't':
+ if (memeq("accep", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT;
+ }
+ break;
+ }
+ break;
+ case 7:
+ switch (name[6]) {
+ case 'c':
+ if (memeq("alt-sv", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_ALT_SVC;
+ }
+ break;
+ case 'd':
+ if (memeq(":metho", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__METHOD;
+ }
+ break;
+ case 'e':
+ if (memeq(":schem", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__SCHEME;
+ }
+ if (memeq("purpos", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_PURPOSE;
+ }
+ if (memeq("upgrad", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_UPGRADE;
+ }
+ break;
+ case 'r':
+ if (memeq("refere", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_REFERER;
+ }
+ break;
+ case 's':
+ if (memeq(":statu", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__STATUS;
+ }
+ break;
+ }
+ break;
+ case 8:
+ switch (name[7]) {
+ case 'e':
+ if (memeq("if-rang", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_IF_RANGE;
+ }
+ break;
+ case 'n':
+ if (memeq("locatio", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_LOCATION;
+ }
+ break;
+ case 'y':
+ if (memeq("priorit", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_PRIORITY;
+ }
+ break;
+ }
+ break;
+ case 9:
+ switch (name[8]) {
+ case 'd':
+ if (memeq("forwarde", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN_FORWARDED;
+ }
+ break;
+ case 'l':
+ if (memeq(":protoco", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN__PROTOCOL;
+ }
+ break;
+ case 't':
+ if (memeq("expect-c", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN_EXPECT_CT;
+ }
+ break;
+ }
+ break;
+ case 10:
+ switch (name[9]) {
+ case 'a':
+ if (memeq("early-dat", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_EARLY_DATA;
+ }
+ break;
+ case 'e':
+ if (memeq("keep-aliv", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_KEEP_ALIVE;
+ }
+ if (memeq("set-cooki", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_SET_COOKIE;
+ }
+ break;
+ case 'n':
+ if (memeq("connectio", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_CONNECTION;
+ }
+ break;
+ case 't':
+ if (memeq("user-agen", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_USER_AGENT;
+ }
+ break;
+ case 'y':
+ if (memeq(":authorit", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN__AUTHORITY;
+ }
+ break;
+ }
+ break;
+ case 12:
+ switch (name[11]) {
+ case 'e':
+ if (memeq("content-typ", name, 11)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_TYPE;
+ }
+ break;
+ }
+ break;
+ case 13:
+ switch (name[12]) {
+ case 'd':
+ if (memeq("last-modifie", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_LAST_MODIFIED;
+ }
+ break;
+ case 'h':
+ if (memeq("if-none-matc", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH;
+ }
+ break;
+ case 'l':
+ if (memeq("cache-contro", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_CACHE_CONTROL;
+ }
+ break;
+ case 'n':
+ if (memeq("authorizatio", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_AUTHORIZATION;
+ }
+ break;
+ case 's':
+ if (memeq("accept-range", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES;
+ }
+ break;
+ }
+ break;
+ case 14:
+ switch (name[13]) {
+ case 'h':
+ if (memeq("content-lengt", name, 13)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH;
+ }
+ break;
+ }
+ break;
+ case 15:
+ switch (name[14]) {
+ case 'e':
+ if (memeq("accept-languag", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE;
+ }
+ break;
+ case 'g':
+ if (memeq("accept-encodin", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING;
+ }
+ break;
+ case 'r':
+ if (memeq("x-forwarded-fo", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR;
+ }
+ break;
+ case 's':
+ if (memeq("x-frame-option", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS;
+ }
+ break;
+ }
+ break;
+ case 16:
+ switch (name[15]) {
+ case 'g':
+ if (memeq("content-encodin", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING;
+ }
+ break;
+ case 'n':
+ if (memeq("proxy-connectio", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION;
+ }
+ if (memeq("x-xss-protectio", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION;
+ }
+ break;
+ }
+ break;
+ case 17:
+ switch (name[16]) {
+ case 'e':
+ if (memeq("if-modified-sinc", name, 16)) {
+ return NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE;
+ }
+ break;
+ case 'g':
+ if (memeq("transfer-encodin", name, 16)) {
+ return NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING;
+ }
+ break;
+ }
+ break;
+ case 19:
+ switch (name[18]) {
+ case 'n':
+ if (memeq("content-dispositio", name, 18)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION;
+ }
+ if (memeq("timing-allow-origi", name, 18)) {
+ return NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN;
+ }
+ break;
+ }
+ break;
+ case 22:
+ switch (name[21]) {
+ case 's':
+ if (memeq("x-content-type-option", name, 21)) {
+ return NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS;
+ }
+ break;
+ }
+ break;
+ case 23:
+ switch (name[22]) {
+ case 'y':
+ if (memeq("content-security-polic", name, 22)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY;
+ }
+ break;
+ }
+ break;
+ case 25:
+ switch (name[24]) {
+ case 's':
+ if (memeq("upgrade-insecure-request", name, 24)) {
+ return NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS;
+ }
+ break;
+ case 'y':
+ if (memeq("strict-transport-securit", name, 24)) {
+ return NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY;
+ }
+ break;
+ }
+ break;
+ case 27:
+ switch (name[26]) {
+ case 'n':
+ if (memeq("access-control-allow-origi", name, 26)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN;
+ }
+ break;
+ }
+ break;
+ case 28:
+ switch (name[27]) {
+ case 's':
+ if (memeq("access-control-allow-header", name, 27)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS;
+ }
+ if (memeq("access-control-allow-method", name, 27)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS;
+ }
+ break;
+ }
+ break;
+ case 29:
+ switch (name[28]) {
+ case 'd':
+ if (memeq("access-control-request-metho", name, 28)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD;
+ }
+ break;
+ case 's':
+ if (memeq("access-control-expose-header", name, 28)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS;
+ }
+ break;
+ }
+ break;
+ case 30:
+ switch (name[29]) {
+ case 's':
+ if (memeq("access-control-request-header", name, 29)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS;
+ }
+ break;
+ }
+ break;
+ case 32:
+ switch (name[31]) {
+ case 's':
+ if (memeq("access-control-allow-credential", name, 31)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS;
+ }
+ break;
+ }
+ break;
+ }
+ return -1;
+}
+
+static size_t table_space(size_t namelen, size_t valuelen) {
+ return NGHTTP3_QPACK_ENTRY_OVERHEAD + namelen + valuelen;
+}
+
+static int qpack_nv_name_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) {
+ return a->name->len == b->namelen &&
+ memeq(a->name->base, b->name, b->namelen);
+}
+
+static int qpack_nv_value_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) {
+ return a->value->len == b->valuelen &&
+ memeq(a->value->base, b->value, b->valuelen);
+}
+
+static void qpack_map_init(nghttp3_qpack_map *map) {
+ memset(map, 0, sizeof(nghttp3_qpack_map));
+}
+
+static void qpack_map_insert(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
+ nghttp3_qpack_entry **bucket;
+
+ bucket = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)];
+
+ if (*bucket == NULL) {
+ *bucket = ent;
+ return;
+ }
+
+ /* larger absidx is linked near the root */
+ ent->map_next = *bucket;
+ *bucket = ent;
+}
+
+static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
+ nghttp3_qpack_entry **dst;
+
+ dst = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)];
+
+ for (; *dst; dst = &(*dst)->map_next) {
+ if (*dst != ent) {
+ continue;
+ }
+
+ *dst = ent->map_next;
+ ent->map_next = NULL;
+ return;
+ }
+}
+
+/*
+ * 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.
+ */
+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;
+}
+
+/* |*ppb_match| (post-base match), if it is not NULL, is always exact
+ match. */
+static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
+ int *exact_match,
+ nghttp3_qpack_entry **pmatch,
+ nghttp3_qpack_entry **ppb_match,
+ const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, uint64_t krcnt,
+ int allow_blocking, int name_only) {
+ nghttp3_qpack_entry *p;
+
+ *exact_match = 0;
+ *pmatch = NULL;
+ *ppb_match = NULL;
+
+ for (p = encoder->dtable_map.table[hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; p;
+ p = p->map_next) {
+ if (token != p->nv.token ||
+ (token == -1 && (hash != p->hash || !qpack_nv_name_eq(&p->nv, nv))) ||
+ !qpack_context_can_reference(&encoder->ctx, p->absidx)) {
+ continue;
+ }
+ if (allow_blocking || p->absidx + 1 <= krcnt) {
+ if (!*pmatch) {
+ *pmatch = p;
+ if (name_only) {
+ return;
+ }
+ }
+ if (qpack_nv_value_eq(&p->nv, nv)) {
+ *pmatch = p;
+ *exact_match = 1;
+ return;
+ }
+ } else if (!*ppb_match && qpack_nv_value_eq(&p->nv, nv)) {
+ *ppb_match = p;
+ }
+ }
+}
+
+/*
+ * qpack_context_init initializes |ctx|. |max_dtable_size| is the
+ * maximum size of dynamic table. |mem| is a memory allocator.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_context_init(nghttp3_qpack_context *ctx,
+ size_t max_dtable_size, size_t max_blocked,
+ const nghttp3_mem *mem) {
+ int rv;
+ size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ size_t len2;
+
+ for (len2 = 1; len2 < len; len2 <<= 1)
+ ;
+
+ rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *),
+ mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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->next_absidx = 0;
+ ctx->bad = 0;
+
+ return 0;
+}
+
+static void qpack_context_free(nghttp3_qpack_context *ctx) {
+ nghttp3_qpack_entry *ent;
+ size_t i, len = nghttp3_ringbuf_len(&ctx->dtable);
+
+ for (i = 0; i < len; ++i) {
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i);
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(ctx->mem, ent);
+ }
+ nghttp3_ringbuf_free(&ctx->dtable);
+}
+
+static int ref_min_cnt_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ nghttp3_qpack_header_block_ref *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, min_cnts_pe);
+ nghttp3_qpack_header_block_ref *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, min_cnts_pe);
+
+ return lhs->min_cnt < rhs->min_cnt;
+}
+
+typedef struct nghttp3_blocked_streams_key {
+ uint64_t max_cnt;
+ uint64_t id;
+} nghttp3_blocked_streams_key;
+
+static int max_cnt_greater(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_blocked_streams_key *a = lhs;
+ const nghttp3_blocked_streams_key *b = rhs;
+ return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id);
+}
+
+int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
+ size_t max_dtable_size, size_t max_blocked,
+ const nghttp3_mem *mem) {
+ int rv;
+
+ rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_map_init(&encoder->streams, mem);
+ if (rv != 0) {
+ goto streams_init_fail;
+ }
+
+ rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
+ sizeof(nghttp3_blocked_streams_key), mem);
+ if (rv != 0) {
+ goto blocked_streams_init_fail;
+ }
+
+ qpack_map_init(&encoder->dtable_map);
+ nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem);
+
+ encoder->krcnt = 0;
+ encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE;
+ encoder->opcode = 0;
+ encoder->min_dtable_update = SIZE_MAX;
+ encoder->last_max_dtable_update = 0;
+ encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE;
+
+ 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) {
+ const nghttp3_mem *mem = ptr;
+ nghttp3_qpack_stream *stream =
+ nghttp3_struct_of(entry, nghttp3_qpack_stream, me);
+ nghttp3_qpack_stream_del(stream, mem);
+ return 0;
+}
+
+void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) {
+ nghttp3_pq_free(&encoder->min_cnts);
+ nghttp3_ksl_free(&encoder->blocked_streams);
+ nghttp3_map_each_free(&encoder->streams, map_stream_free,
+ (void *)encoder->ctx.mem);
+ nghttp3_map_free(&encoder->streams);
+ 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;
+ }
+
+ if (encoder->ctx.max_dtable_size == max_dtable_size) {
+ return 0;
+ }
+
+ 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;
+ }
+ encoder->last_max_dtable_update = max_dtable_size;
+
+ return 0;
+}
+
+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;
+}
+
+uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) {
+ assert(!nghttp3_pq_empty(&encoder->min_cnts));
+
+ return nghttp3_struct_of(nghttp3_pq_top(&encoder->min_cnts),
+ nghttp3_qpack_header_block_ref, min_cnts_pe)
+ ->min_cnt;
+}
+
+void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) {
+ nghttp3_ringbuf *dtable = &encoder->ctx.dtable;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ uint64_t min_cnt = UINT64_MAX;
+ size_t len;
+ nghttp3_qpack_entry *ent;
+
+ if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) {
+ return;
+ }
+
+ if (!nghttp3_pq_empty(&encoder->min_cnts)) {
+ min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
+ }
+
+ for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) {
+ len = nghttp3_ringbuf_len(dtable);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
+ if (ent->absidx + 1 == min_cnt) {
+ return;
+ }
+
+ encoder->ctx.dtable_size -=
+ table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(dtable);
+ qpack_map_remove(&encoder->dtable_map, ent);
+
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+}
+
+/*
+ * qpack_encoder_add_stream_ref adds another dynamic table reference
+ * to a stream denoted by |stream_id|. |stream| must be NULL if no
+ * stream object is not found for the given stream ID. |max_cnt| and
+ * |min_cnt| is the maximum and minimum insert count it references
+ * respectively.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id,
+ nghttp3_qpack_stream *stream,
+ uint64_t max_cnt, uint64_t min_cnt) {
+ nghttp3_qpack_header_block_ref *ref;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ uint64_t prev_max_cnt = 0;
+ int rv;
+
+ if (stream == NULL) {
+ rv = nghttp3_qpack_stream_new(&stream, stream_id, mem);
+ if (rv != 0) {
+ assert(rv == NGHTTP3_ERR_NOMEM);
+ return rv;
+ }
+ rv = nghttp3_map_insert(&encoder->streams, &stream->me);
+ if (rv != 0) {
+ assert(rv == NGHTTP3_ERR_NOMEM);
+ nghttp3_qpack_stream_del(stream, mem);
+ return rv;
+ }
+ } else {
+ prev_max_cnt = nghttp3_qpack_stream_get_max_cnt(stream);
+ if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream) &&
+ max_cnt > prev_max_cnt) {
+ nghttp3_qpack_encoder_unblock_stream(encoder, stream);
+ }
+ }
+
+ rv = nghttp3_qpack_header_block_ref_new(&ref, max_cnt, min_cnt, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_qpack_stream_add_ref(stream, ref);
+ if (rv != 0) {
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+ return rv;
+ }
+
+ if (max_cnt > prev_max_cnt &&
+ nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) {
+ rv = nghttp3_qpack_encoder_block_stream(encoder, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nghttp3_pq_push(&encoder->min_cnts, &ref->min_cnts_pe);
+}
+
+static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ size_t i, len;
+ nghttp3_qpack_header_block_ref *ref;
+
+ nghttp3_map_remove(&encoder->streams, stream->me.key);
+
+ len = nghttp3_ringbuf_len(&stream->refs);
+ for (i = 0; i < len; ++i) {
+ ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs,
+ i);
+
+ assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe);
+ }
+}
+
+/*
+ * reserve_buf_internal ensures that |buf| contains at least
+ * |extra_size| of free space. In other words, if this function
+ * succeeds, nghttp2_buf_left(buf) >= extra_size holds. |min_size| is
+ * the minimum size of buffer. The allocated buffer has at least
+ * |min_size| bytes.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int reserve_buf_internal(nghttp3_buf *buf, size_t extra_size,
+ size_t min_size, const nghttp3_mem *mem) {
+ size_t left = nghttp3_buf_left(buf);
+ size_t n = min_size, need;
+
+ if (left >= extra_size) {
+ return 0;
+ }
+
+ need = nghttp3_buf_cap(buf) + extra_size - left;
+
+ for (; n < need; n *= 2)
+ ;
+
+ return nghttp3_buf_reserve(buf, n, mem);
+}
+
+static int reserve_buf_small(nghttp3_buf *buf, size_t extra_size,
+ const nghttp3_mem *mem) {
+ return reserve_buf_internal(buf, extra_size, 32, mem);
+}
+
+static int reserve_buf(nghttp3_buf *buf, size_t extra_size,
+ const nghttp3_mem *mem) {
+ return reserve_buf_internal(buf, extra_size, 32, mem);
+}
+
+int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *pbuf, nghttp3_buf *rbuf,
+ nghttp3_buf *ebuf, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ size_t i;
+ uint64_t max_cnt = 0, min_cnt = UINT64_MAX;
+ uint64_t base;
+ int rv = 0;
+ int allow_blocking;
+ int blocked_stream;
+ nghttp3_qpack_stream *stream;
+
+ if (encoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = nghttp3_qpack_encoder_process_dtable_update(encoder, ebuf);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ base = encoder->ctx.next_absidx;
+
+ stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id);
+ 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);
+
+ DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id,
+ blocked_stream, allow_blocking);
+
+ for (i = 0; i < nvlen; ++i) {
+ rv = nghttp3_qpack_encoder_encode_nv(encoder, &max_cnt, &min_cnt, rbuf,
+ ebuf, &nva[i], base, allow_blocking);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ nghttp3_qpack_encoder_write_field_section_prefix(encoder, pbuf, max_cnt,
+ base);
+
+ /* TODO If max_cnt == 0, no reference is made to dtable. */
+ if (!max_cnt) {
+ return 0;
+ }
+
+ rv = qpack_encoder_add_stream_ref(encoder, stream_id, stream, max_cnt,
+ min_cnt);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ encoder->ctx.bad = 1;
+ return rv;
+}
+
+/*
+ * qpack_write_number writes variable integer to |rbuf|. |num| is an
+ * integer to write. |prefix| is a prefix of variable integer
+ * encoding.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_write_number(nghttp3_buf *rbuf, uint8_t fb, uint64_t num,
+ size_t prefix, const nghttp3_mem *mem) {
+ int rv;
+ size_t len = nghttp3_qpack_put_varint_len(num, prefix);
+ uint8_t *p;
+
+ rv = reserve_buf(rbuf, len, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = rbuf->last;
+
+ *p = fb;
+ p = nghttp3_qpack_put_varint(p, num, prefix);
+
+ assert((size_t)(p - rbuf->last) == len);
+
+ rbuf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf) {
+ int rv;
+
+ nghttp3_qpack_encoder_shrink_dtable(encoder);
+
+ if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size ||
+ !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) {
+ return 0;
+ }
+
+ if (encoder->min_dtable_update < encoder->last_max_dtable_update) {
+ rv = nghttp3_qpack_encoder_write_set_dtable_cap(encoder, ebuf,
+ encoder->min_dtable_update);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = nghttp3_qpack_encoder_write_set_dtable_cap(
+ encoder, ebuf, encoder->last_max_dtable_update);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf, size_t cap) {
+ DEBUGF("qpack::encode: Set Dynamic Table Capacity capacity=%zu\n", cap);
+ return qpack_write_number(ebuf, 0x20, cap, 5, encoder->ctx.mem);
+}
+
+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);
+}
+
+int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ return stream && encoder->krcnt < nghttp3_qpack_stream_get_max_cnt(stream);
+}
+
+/*
+ * qpack_encoder_decide_indexing_mode determines and returns indexing
+ * mode for header field |nv|. |token| is a token of header field
+ * name.
+ */
+static nghttp3_qpack_indexing_mode
+qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv, int32_t token) {
+ if (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) {
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ }
+
+ switch (token) {
+ case NGHTTP3_QPACK_TOKEN_AUTHORIZATION:
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ case NGHTTP3_QPACK_TOKEN_COOKIE:
+ if (nv->valuelen < 20) {
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ }
+ break;
+ case -1:
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ case NGHTTP3_QPACK_TOKEN_AGE:
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH:
+ case NGHTTP3_QPACK_TOKEN_ETAG:
+ case NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE:
+ case NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH:
+ case NGHTTP3_QPACK_TOKEN_LOCATION:
+ case NGHTTP3_QPACK_TOKEN_SET_COOKIE:
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ case NGHTTP3_QPACK_TOKEN_TE:
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ break;
+ default:
+ if (token >= 1000) {
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ }
+ }
+
+ if (table_space(nv->namelen, nv->valuelen) >
+ encoder->ctx.max_dtable_size * 3 / 4) {
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ }
+
+ return NGHTTP3_QPACK_INDEXING_MODE_STORE;
+}
+
+/*
+ * qpack_encoder_can_index returns nonzero if an entry which occupies
+ * |need| bytes can be inserted into dynamic table. |min_cnt| is the
+ * minimum insert count which blocked stream requires.
+ */
+static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need,
+ uint64_t min_cnt) {
+ size_t avail = 0;
+ size_t len;
+ uint64_t gmin_cnt;
+ 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 (need <= avail) {
+ return 1;
+ }
+ }
+
+ if (!nghttp3_pq_empty(&encoder->min_cnts)) {
+ gmin_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
+ min_cnt = nghttp3_min(min_cnt, gmin_cnt);
+ }
+
+ if (min_cnt == UINT64_MAX) {
+ return encoder->ctx.max_dtable_size >= need;
+ }
+
+ min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1);
+
+ len = nghttp3_ringbuf_len(&encoder->ctx.dtable);
+ assert(len);
+ last_ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
+
+ if (min_ent == last_ent) {
+ return 0;
+ }
+
+ return avail + min_ent->sum - last_ent->sum >= need;
+}
+
+/*
+ * qpack_encoder_can_index_nv returns nonzero if header field |nv| can
+ * be inserted into dynamic table. |min_cnt| is the minimum insert
+ * count which blocked stream requires.
+ */
+static int qpack_encoder_can_index_nv(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv, uint64_t min_cnt) {
+ return qpack_encoder_can_index(
+ encoder, table_space(nv->namelen, nv->valuelen), min_cnt);
+}
+
+/*
+ * qpack_encoder_can_index_duplicate returns nonzero if an entry at
+ * |absidx| in dynamic table can be inserted to dynamic table as
+ * duplicate. |min_cnt| is the minimum insert count which blocked
+ * stream requires.
+ */
+static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ uint64_t min_cnt) {
+ nghttp3_qpack_entry *ent =
+ nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ return qpack_encoder_can_index(
+ encoder, table_space(ent->nv.name->len, ent->nv.value->len), min_cnt);
+}
+
+/*
+ * qpack_context_check_draining returns nonzero if an entry at
+ * |absidx| in dynamic table is one of draining entries.
+ */
+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);
+ nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
+
+ return ctx->dtable_sum - ent->sum > safe;
+}
+
+int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
+ uint64_t *pmax_cnt, uint64_t *pmin_cnt,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ const nghttp3_nv *nv, uint64_t base,
+ int allow_blocking) {
+ uint32_t hash = 0;
+ int32_t token;
+ nghttp3_qpack_indexing_mode indexing_mode;
+ nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1};
+ nghttp3_qpack_entry *new_ent = NULL;
+ int static_entry;
+ int just_index = 0;
+ int rv;
+
+ token = qpack_lookup_token(nv->name, nv->namelen);
+ static_entry = token != -1 && (size_t)token < nghttp3_arraylen(token_stable);
+ if (static_entry) {
+ hash = token_stable[token].hash;
+ } else {
+ switch (token) {
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ hash = 2952701295u;
+ break;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ hash = 1011170994u;
+ break;
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ hash = 1128642621u;
+ break;
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ hash = 2498028297u;
+ break;
+ }
+ }
+
+ indexing_mode = qpack_encoder_decide_indexing_mode(encoder, nv, token);
+
+ if (static_entry) {
+ sres = nghttp3_qpack_lookup_stable(nv, token, indexing_mode);
+ if (sres.index != -1 && sres.name_value_match) {
+ return nghttp3_qpack_encoder_write_static_indexed(encoder, rbuf,
+ (size_t)sres.index);
+ }
+ }
+
+ if (hash &&
+ nghttp3_map_size(&encoder->streams) < NGHTTP3_QPACK_MAX_QPACK_STREAMS) {
+ dres = nghttp3_qpack_encoder_lookup_dtable(encoder, nv, token, hash,
+ indexing_mode, encoder->krcnt,
+ allow_blocking);
+ just_index = indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_STORE &&
+ dres.pb_index == -1;
+ }
+
+ if (dres.index != -1 && dres.name_value_match) {
+ if (allow_blocking &&
+ qpack_context_check_draining(&encoder->ctx, (size_t)dres.index) &&
+ qpack_encoder_can_index_duplicate(encoder, (size_t)dres.index,
+ *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_write_duplicate_insert(encoder, ebuf,
+ (size_t)dres.index);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_dtable_duplicate_add(encoder,
+ (size_t)dres.index);
+ if (rv != 0) {
+ return rv;
+ }
+
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ dres.index = (nghttp3_ssize)new_ent->absidx;
+ }
+ *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1));
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1));
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, (size_t)dres.index, base);
+ }
+
+ if (sres.index != -1) {
+ if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_write_static_insert(encoder, ebuf,
+ (size_t)sres.index, nv);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_dtable_static_add(encoder, (size_t)sres.index,
+ nv, hash);
+ if (rv != 0) {
+ return rv;
+ }
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, new_ent->absidx, base);
+ }
+ }
+
+ return nghttp3_qpack_encoder_write_static_indexed_name(
+ encoder, rbuf, (size_t)sres.index, nv);
+ }
+
+ if (dres.index != -1) {
+ if (just_index &&
+ qpack_encoder_can_index_nv(
+ encoder, nv,
+ allow_blocking ? *pmin_cnt
+ : nghttp3_min((size_t)dres.index + 1, *pmin_cnt))) {
+ rv = nghttp3_qpack_encoder_write_dynamic_insert(encoder, ebuf,
+ (size_t)dres.index, nv);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!allow_blocking) {
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)dres.index + 1);
+ }
+
+ rv = nghttp3_qpack_encoder_dtable_dynamic_add(encoder, (size_t)dres.index,
+ nv, hash);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, new_ent->absidx, base);
+ }
+ }
+
+ *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1));
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1));
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ encoder, rbuf, (size_t)dres.index, base, nv);
+ }
+
+ if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_dtable_literal_add(encoder, nv, token, hash);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_write_literal_insert(encoder, ebuf, nv);
+ if (rv != 0) {
+ return rv;
+ }
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(encoder, rbuf,
+ new_ent->absidx, base);
+ }
+ }
+
+ return nghttp3_qpack_encoder_write_literal(encoder, rbuf, nv);
+}
+
+nghttp3_qpack_lookup_result
+nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token,
+ nghttp3_qpack_indexing_mode indexing_mode) {
+ nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx,
+ 0, -1};
+ nghttp3_qpack_static_entry *ent;
+ nghttp3_qpack_static_header *hdr;
+ size_t i;
+
+ assert(token >= 0);
+
+ if (indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER) {
+ return res;
+ }
+
+ for (i = (size_t)token;
+ i < nghttp3_arraylen(token_stable) && token_stable[i].token == token;
+ ++i) {
+ ent = &token_stable[i];
+ hdr = &stable[ent->absidx];
+ if (hdr->value.len == nv->valuelen &&
+ memeq(hdr->value.base, nv->value, nv->valuelen)) {
+ res.index = (nghttp3_ssize)ent->absidx;
+ res.name_value_match = 1;
+ return res;
+ }
+ }
+ return res;
+}
+
+nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable(
+ nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt,
+ int allow_blocking) {
+ nghttp3_qpack_lookup_result res = {-1, 0, -1};
+ int exact_match = 0;
+ nghttp3_qpack_entry *match, *pb_match;
+
+ encoder_qpack_map_find(encoder, &exact_match, &match, &pb_match, nv, token,
+ hash, krcnt, allow_blocking,
+ indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER);
+ if (match) {
+ res.index = (nghttp3_ssize)match->absidx;
+ res.name_value_match = exact_match;
+ }
+ if (pb_match) {
+ res.pb_index = (nghttp3_ssize)pb_match->absidx;
+ }
+
+ return res;
+}
+
+int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref,
+ uint64_t max_cnt, uint64_t min_cnt,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_header_block_ref *ref =
+ nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_header_block_ref));
+
+ if (ref == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ ref->max_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ ref->min_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ ref->max_cnt = max_cnt;
+ ref->min_cnt = min_cnt;
+
+ *pref = ref;
+
+ return 0;
+}
+
+void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref,
+ const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, ref);
+}
+
+static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ const nghttp3_qpack_header_block_ref *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, max_cnts_pe);
+ const nghttp3_qpack_header_block_ref *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, max_cnts_pe);
+
+ return lhs->max_cnt > rhs->max_cnt;
+}
+
+int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_stream *stream;
+
+ stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream));
+ if (stream == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_ringbuf_init(&stream->refs, 4,
+ sizeof(nghttp3_qpack_header_block_ref *), mem);
+ if (rv != 0) {
+ nghttp3_mem_free(mem, stream);
+ return rv;
+ }
+
+ nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem);
+
+ stream->me.key = (uint64_t)stream_id;
+
+ *pstream = stream;
+
+ return 0;
+}
+
+void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_header_block_ref *ref;
+ size_t i, len;
+
+ if (stream == NULL) {
+ return;
+ }
+
+ nghttp3_pq_free(&stream->max_cnts);
+
+ len = nghttp3_ringbuf_len(&stream->refs);
+ for (i = 0; i < len; ++i) {
+ ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs,
+ i);
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+ }
+
+ nghttp3_ringbuf_free(&stream->refs);
+
+ nghttp3_mem_free(mem, stream);
+}
+
+uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream) {
+ nghttp3_qpack_header_block_ref *ref;
+
+ if (nghttp3_pq_empty(&stream->max_cnts)) {
+ return 0;
+ }
+
+ ref = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe);
+ return ref->max_cnt;
+}
+
+int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream,
+ nghttp3_qpack_header_block_ref *ref) {
+ nghttp3_qpack_header_block_ref **dest;
+ int rv;
+
+ if (nghttp3_ringbuf_full(&stream->refs)) {
+ rv = nghttp3_ringbuf_reserve(&stream->refs,
+ nghttp3_ringbuf_len(&stream->refs) * 2);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(&stream->refs);
+ *dest = ref;
+
+ return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe);
+}
+
+void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) {
+ nghttp3_qpack_header_block_ref *ref;
+
+ assert(nghttp3_ringbuf_len(&stream->refs));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ assert(ref->max_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&stream->max_cnts, &ref->max_cnts_pe);
+
+ nghttp3_ringbuf_pop_front(&stream->refs);
+}
+
+int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx) {
+ DEBUGF("qpack::encode: Indexed Field Line (static) absidx=%" PRIu64 "\n",
+ absidx);
+ return qpack_write_number(rbuf, 0xc0, absidx, 6, encoder->ctx.mem);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx,
+ uint64_t base) {
+ DEBUGF("qpack::encode: Indexed Field Line (dynamic) absidx=%" PRIu64
+ " base=%" PRIu64 "\n",
+ absidx, base);
+
+ if (absidx < base) {
+ return qpack_write_number(rbuf, 0x80, base - absidx - 1, 6,
+ encoder->ctx.mem);
+ }
+
+ return qpack_write_number(rbuf, 0x10, absidx - base, 4, encoder->ctx.mem);
+}
+
+/*
+ * qpack_encoder_write_indexed_name writes generic indexed name. |fb|
+ * is the first byte. |nameidx| is an index of referenced name.
+ * |prefix| is a prefix of variable integer encoding. |nv| is a
+ * header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *buf, uint8_t fb,
+ uint64_t nameidx, size_t prefix,
+ const nghttp3_nv *nv) {
+ int rv;
+ size_t len = nghttp3_qpack_put_varint_len(nameidx, prefix);
+ uint8_t *p;
+ size_t hlen;
+ int h = 0;
+
+ hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
+ if (hlen < nv->valuelen) {
+ h = 1;
+ len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen;
+ } else {
+ len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen;
+ }
+
+ rv = reserve_buf(buf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = buf->last;
+
+ *p = fb;
+ p = nghttp3_qpack_put_varint(p, nameidx, prefix);
+
+ if (h) {
+ *p = 0x80;
+ p = nghttp3_qpack_put_varint(p, hlen, 7);
+ p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen);
+ } else {
+ *p = 0;
+ p = nghttp3_qpack_put_varint(p, nv->valuelen, 7);
+ if (nv->valuelen) {
+ p = nghttp3_cpymem(p, nv->value, nv->valuelen);
+ }
+ }
+
+ assert((size_t)(p - buf->last) == len);
+
+ buf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_static_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ const nghttp3_nv *nv) {
+ uint8_t fb =
+ (uint8_t)(0x50 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0));
+
+ DEBUGF("qpack::encode: Literal Field Line With Name Reference (static) "
+ "absidx=%" PRIu64 " never=%d\n",
+ absidx, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0);
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx, 4, nv);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ uint64_t base, const nghttp3_nv *nv) {
+ uint8_t fb;
+
+ DEBUGF("qpack::encode: Literal Field Line With Name Reference (dynamic) "
+ "absidx=%" PRIu64 " base=%" PRIu64 " never=%d\n",
+ absidx, base, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0);
+
+ if (absidx < base) {
+ fb = (uint8_t)(0x40 |
+ ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0));
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb,
+ base - absidx - 1, 4, nv);
+ }
+
+ fb = (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x08 : 0;
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx - base, 3,
+ nv);
+}
+
+/*
+ * qpack_encoder_write_literal writes generic literal header field
+ * representation. |fb| is a first byte. |prefix| is a prefix of
+ * variable integer encoding for name length. |nv| is a header field
+ * to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *buf, uint8_t fb,
+ size_t prefix, const nghttp3_nv *nv) {
+ int rv;
+ size_t len;
+ uint8_t *p;
+ size_t nhlen, vhlen;
+ int nh = 0, vh = 0;
+
+ nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen);
+ if (nhlen < nv->namelen) {
+ nh = 1;
+ len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen;
+ } else {
+ len = nghttp3_qpack_put_varint_len(nv->namelen, prefix) + nv->namelen;
+ }
+
+ vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
+ if (vhlen < nv->valuelen) {
+ vh = 1;
+ len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen;
+ } else {
+ len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen;
+ }
+
+ rv = reserve_buf(buf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = buf->last;
+
+ *p = fb;
+ if (nh) {
+ *p |= (uint8_t)(1 << prefix);
+ p = nghttp3_qpack_put_varint(p, nhlen, prefix);
+ p = nghttp3_qpack_huffman_encode(p, nv->name, nv->namelen);
+ } else {
+ p = nghttp3_qpack_put_varint(p, nv->namelen, prefix);
+ if (nv->namelen) {
+ p = nghttp3_cpymem(p, nv->name, nv->namelen);
+ }
+ }
+
+ *p = 0;
+
+ if (vh) {
+ *p |= 0x80;
+ p = nghttp3_qpack_put_varint(p, vhlen, 7);
+ p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen);
+ } else {
+ p = nghttp3_qpack_put_varint(p, nv->valuelen, 7);
+ if (nv->valuelen) {
+ p = nghttp3_cpymem(p, nv->value, nv->valuelen);
+ }
+ }
+
+ assert((size_t)(p - buf->last) == len);
+
+ buf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ const nghttp3_nv *nv) {
+ uint8_t fb =
+ (uint8_t)(0x20 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x10 : 0));
+
+ DEBUGF("qpack::encode: Literal Field Line With Literal Name\n");
+ return qpack_encoder_write_literal(encoder, rbuf, fb, 3, nv);
+}
+
+int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%" PRIu64
+ "\n",
+ absidx);
+ return qpack_encoder_write_indexed_name(encoder, ebuf, 0xc0, absidx, 6, nv);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%" PRIu64
+ "\n",
+ absidx);
+ return qpack_encoder_write_indexed_name(
+ encoder, ebuf, 0x80, encoder->ctx.next_absidx - absidx - 1, 6, nv);
+}
+
+int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx) {
+ uint64_t idx = encoder->ctx.next_absidx - absidx - 1;
+ size_t len = nghttp3_qpack_put_varint_len(idx, 5);
+ uint8_t *p;
+ int rv;
+
+ DEBUGF("qpack::encode: Insert duplicate absidx=%" PRIu64 "\n", absidx);
+
+ rv = reserve_buf(ebuf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = ebuf->last;
+
+ *p = 0;
+ p = nghttp3_qpack_put_varint(p, idx, 5);
+
+ assert((size_t)(p - ebuf->last) == len);
+
+ ebuf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Literal Name\n");
+ return qpack_encoder_write_literal(encoder, ebuf, 0x40, 5, nv);
+}
+
+int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
+ nghttp3_qpack_nv *qnv,
+ nghttp3_qpack_map *dtable_map,
+ uint32_t hash) {
+ nghttp3_qpack_entry *new_ent, **p, *ent;
+ const nghttp3_mem *mem = ctx->mem;
+ size_t space;
+ size_t i;
+ int rv;
+
+ space = table_space(qnv->name->len, qnv->value->len);
+
+ assert(space <= ctx->max_dtable_size);
+
+ while (ctx->dtable_size + space > ctx->max_dtable_size) {
+ i = nghttp3_ringbuf_len(&ctx->dtable);
+ assert(i);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
+
+ ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(&ctx->dtable);
+ if (dtable_map) {
+ qpack_map_remove(dtable_map, ent);
+ }
+
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+
+ new_ent = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_entry));
+ if (new_ent == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_qpack_entry_init(new_ent, qnv, ctx->dtable_sum, ctx->next_absidx++,
+ hash);
+
+ if (nghttp3_ringbuf_full(&ctx->dtable)) {
+ rv = nghttp3_ringbuf_reserve(&ctx->dtable,
+ nghttp3_ringbuf_len(&ctx->dtable) * 2);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ p = nghttp3_ringbuf_push_front(&ctx->dtable);
+ *p = new_ent;
+
+ if (dtable_map) {
+ qpack_map_insert(dtable_map, new_ent);
+ }
+
+ ctx->dtable_size += space;
+ ctx->dtable_sum += space;
+
+ return 0;
+
+fail:
+ nghttp3_qpack_entry_free(new_ent);
+ nghttp3_mem_free(mem, new_ent);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash) {
+ const nghttp3_qpack_static_header *shd;
+ nghttp3_qpack_nv qnv;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ assert(nghttp3_arraylen(stable) > absidx);
+
+ shd = &stable[absidx];
+
+ qnv.name = (nghttp3_rcbuf *)&shd->name;
+ qnv.token = shd->token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash) {
+ nghttp3_qpack_nv qnv;
+ nghttp3_qpack_entry *ent;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ qnv.name = ent->nv.name;
+ qnv.token = ent->nv.token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(qnv.name);
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx) {
+ nghttp3_qpack_nv qnv;
+ nghttp3_qpack_entry *ent;
+ int rv;
+
+ ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ qnv = ent->nv;
+ nghttp3_rcbuf_incref(qnv.name);
+ nghttp3_rcbuf_incref(qnv.value);
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, ent->hash);
+
+ nghttp3_rcbuf_decref(qnv.name);
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv,
+ int32_t token, uint32_t hash) {
+ nghttp3_qpack_nv qnv;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.name, nv->name, nv->namelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ nghttp3_rcbuf_decref(qnv.name);
+ return rv;
+ }
+
+ qnv.token = token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx) {
+ size_t relidx;
+
+ assert(ctx->next_absidx > absidx);
+ assert(ctx->next_absidx - absidx - 1 < nghttp3_ringbuf_len(&ctx->dtable));
+
+ relidx = (size_t)(ctx->next_absidx - absidx - 1);
+
+ return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, relidx);
+}
+
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx) {
+ assert(nghttp3_ringbuf_len(&ctx->dtable));
+ return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, 0);
+}
+
+void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv,
+ size_t sum, uint64_t absidx, uint32_t hash) {
+ ent->nv = *qnv;
+ ent->map_next = NULL;
+ ent->sum = sum;
+ ent->absidx = absidx;
+ ent->hash = hash;
+
+ nghttp3_rcbuf_incref(ent->nv.name);
+ nghttp3_rcbuf_incref(ent->nv.value);
+}
+
+void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) {
+ nghttp3_rcbuf_decref(ent->nv.value);
+ nghttp3_rcbuf_decref(ent->nv.name);
+}
+
+int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ nghttp3_blocked_streams_key bsk = {
+ nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe)
+ ->max_cnt,
+ stream->me.key};
+
+ return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream);
+}
+
+void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ nghttp3_blocked_streams_key bsk = {
+ nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe)
+ ->max_cnt,
+ stream->me.key};
+ nghttp3_ksl_it it;
+
+ /* This is purely debugging purpose only */
+ it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk);
+
+ assert(!nghttp3_ksl_it_end(&it));
+ assert(nghttp3_ksl_it_get(&it) == stream);
+
+ nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &bsk);
+}
+
+void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
+ uint64_t max_cnt) {
+ nghttp3_blocked_streams_key bsk = {max_cnt, 0};
+ nghttp3_ksl_it it;
+
+ it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk);
+
+ for (; !nghttp3_ksl_it_end(&it);) {
+ bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it);
+ nghttp3_ksl_remove(&encoder->blocked_streams, &it, &bsk);
+ }
+}
+
+void 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;
+ }
+
+ assert(nghttp3_ringbuf_len(&stream->refs));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ DEBUGF("qpack::encoder: Header acknowledgement stream=%ld ricnt=%" PRIu64
+ " krcnt=%" PRIu64 "\n",
+ stream_id, ref->max_cnt, encoder->krcnt);
+
+ if (encoder->krcnt < ref->max_cnt) {
+ encoder->krcnt = ref->max_cnt;
+
+ nghttp3_qpack_encoder_unblock(encoder, ref->max_cnt);
+ }
+
+ nghttp3_qpack_stream_pop_ref(stream);
+
+ assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe);
+
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+
+ if (nghttp3_ringbuf_len(&stream->refs)) {
+ return;
+ }
+
+ qpack_encoder_remove_stream(encoder, stream);
+
+ nghttp3_qpack_stream_del(stream, mem);
+}
+
+int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder,
+ uint64_t n) {
+ if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) {
+ return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ }
+ encoder->krcnt += n;
+
+ nghttp3_qpack_encoder_unblock(encoder, encoder->krcnt);
+
+ return 0;
+}
+
+void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) {
+ encoder->krcnt = encoder->ctx.next_absidx;
+
+ nghttp3_ksl_clear(&encoder->blocked_streams);
+ nghttp3_pq_clear(&encoder->min_cnts);
+ nghttp3_map_each_free(&encoder->streams, map_stream_free,
+ (void *)encoder->ctx.mem);
+}
+
+void nghttp3_qpack_encoder_cancel_stream(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;
+
+ if (stream == NULL) {
+ return;
+ }
+
+ if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) {
+ nghttp3_qpack_encoder_unblock_stream(encoder, stream);
+ }
+
+ qpack_encoder_remove_stream(encoder, stream);
+
+ nghttp3_qpack_stream_del(stream, mem);
+}
+
+size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) {
+ return nghttp3_ksl_len(&encoder->blocked_streams);
+}
+
+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;
+ 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;
+ size_t len = nghttp3_qpack_put_varint_len(encricnt, 8) +
+ nghttp3_qpack_put_varint_len(delta_base, 7);
+ uint8_t *p;
+ int rv;
+
+ DEBUGF("qpack::encode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n",
+ ricnt, base, encoder->ctx.next_absidx);
+
+ rv = reserve_buf(pbuf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = pbuf->last;
+
+ p = nghttp3_qpack_put_varint(p, encricnt, 8);
+ if (sign) {
+ *p = 0x80;
+ } else {
+ *p = 0;
+ }
+ p = nghttp3_qpack_put_varint(p, delta_base, 7);
+
+ assert((size_t)(p - pbuf->last) == len);
+
+ pbuf->last = p;
+
+ return 0;
+}
+
+/*
+ * qpack_read_varint reads |rstate->prefix| prefixed integer stored
+ * from |begin|. The |end| represents the 1 beyond the last of the
+ * valid contiguous memory region from |begin|. The decoded integer
+ * must be less than or equal to NGHTTP3_QPACK_INT_MAX.
+ *
+ * If the |rstate->left| is nonzero, it is used as an initial value,
+ * and this function assumes the |begin| starts with intermediate
+ * data. |rstate->shift| is used as initial integer shift.
+ *
+ * If an entire integer is decoded successfully, the |*fin| is set to
+ * nonzero.
+ *
+ * This function stores the decoded integer in |rstate->left| if it
+ * succeeds, including partial decoding (in this case, number of shift
+ * to make in the next call will be stored in |rstate->shift|) and
+ * returns number of bytes processed, or returns negative error code
+ * NGHTTP3_ERR_QPACK_FATAL, indicating decoding error.
+ */
+static nghttp3_ssize qpack_read_varint(int *fin,
+ nghttp3_qpack_read_state *rstate,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ uint64_t k = (uint8_t)((1 << rstate->prefix) - 1);
+ uint64_t n = rstate->left;
+ uint64_t add;
+ const uint8_t *p = begin;
+ size_t shift = rstate->shift;
+
+ rstate->shift = 0;
+ *fin = 0;
+
+ if (n == 0) {
+ if (((*p) & k) != k) {
+ rstate->left = (*p) & k;
+ *fin = 1;
+ return 1;
+ }
+
+ n = k;
+
+ if (++p == end) {
+ rstate->left = n;
+ return (nghttp3_ssize)(p - begin);
+ }
+ }
+
+ for (; p != end; ++p, shift += 7) {
+ add = (*p) & 0x7f;
+
+ if (shift > 62) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if ((NGHTTP3_QPACK_INT_MAX >> shift) < add) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ add <<= shift;
+
+ if (NGHTTP3_QPACK_INT_MAX - add < n) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ n += add;
+
+ if (((*p) & (1 << 7)) == 0) {
+ break;
+ }
+ }
+
+ rstate->shift = shift;
+
+ if (p == end) {
+ rstate->left = n;
+ return (nghttp3_ssize)(p - begin);
+ }
+
+ rstate->left = n;
+ *fin = 1;
+ return (nghttp3_ssize)(p + 1 - begin);
+}
+
+nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder,
+ const uint8_t *src,
+ size_t srclen) {
+ const uint8_t *p = src, *end;
+ int rv;
+ nghttp3_ssize nread;
+ int rfin;
+
+ if (encoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ end = src + srclen;
+
+ for (; p != end;) {
+ switch (encoder->state) {
+ case NGHTTP3_QPACK_DS_STATE_OPCODE:
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::encode: OPCODE_SECTION_ACK\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK;
+ encoder->rstate.prefix = 7;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::encode: OPCODE_STREAM_CANCEL\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL;
+ encoder->rstate.prefix = 6;
+ } else {
+ DEBUGF("qpack::encode: OPCODE_ICNT_INCREMENT\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT;
+ encoder->rstate.prefix = 6;
+ }
+ encoder->state = NGHTTP3_QPACK_DS_STATE_READ_NUMBER;
+ /* fall through */
+ case NGHTTP3_QPACK_DS_STATE_READ_NUMBER:
+ nread = qpack_read_varint(&rfin, &encoder->rstate, p, end);
+ if (nread < 0) {
+ assert(nread == NGHTTP3_ERR_QPACK_FATAL);
+ rv = NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ switch (encoder->opcode) {
+ case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT:
+ rv = nghttp3_qpack_encoder_add_insert_count(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);
+ break;
+ case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL:
+ nghttp3_qpack_encoder_cancel_stream(encoder,
+ (int64_t)encoder->rstate.left);
+ break;
+ default:
+ /* unreachable */
+ assert(0);
+ break;
+ }
+
+ encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&encoder->rstate);
+ break;
+ default:
+ /* unreachable */
+ assert(0);
+ break;
+ }
+ }
+
+ return p - src;
+
+fail:
+ encoder->ctx.bad = 1;
+ return rv;
+}
+
+size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix) {
+ size_t k = (size_t)((1 << prefix) - 1);
+ size_t len = 0;
+
+ if (n < k) {
+ return 1;
+ }
+
+ n -= k;
+ ++len;
+
+ for (; n >= 128; n >>= 7, ++len)
+ ;
+
+ return len + 1;
+}
+
+uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix) {
+ size_t k = (size_t)((1 << prefix) - 1);
+
+ *buf = (uint8_t)(*buf & ~k);
+
+ if (n < k) {
+ *buf = (uint8_t)(*buf | n);
+ return buf + 1;
+ }
+
+ *buf = (uint8_t)(*buf | k);
+ ++buf;
+
+ n -= k;
+
+ for (; n >= 128; n >>= 7) {
+ *buf++ = (uint8_t)((1 << 7) | (n & 0x7f));
+ }
+
+ *buf++ = (uint8_t)n;
+
+ return buf;
+}
+
+void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate) {
+ nghttp3_rcbuf_decref(rstate->value);
+ nghttp3_rcbuf_decref(rstate->name);
+}
+
+void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) {
+ rstate->name = NULL;
+ rstate->value = NULL;
+ nghttp3_buf_init(&rstate->namebuf);
+ nghttp3_buf_init(&rstate->valuebuf);
+ rstate->left = 0;
+ rstate->prefix = 0;
+ rstate->shift = 0;
+ rstate->absidx = 0;
+ rstate->never = 0;
+ rstate->dynamic = 0;
+ rstate->huffman_encoded = 0;
+}
+
+int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
+ size_t max_dtable_size, size_t max_blocked,
+ const nghttp3_mem *mem) {
+ int rv;
+
+ rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ decoder->opcode = 0;
+ decoder->written_icnt = 0;
+ decoder->max_concurrent_streams = 0;
+
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ nghttp3_buf_init(&decoder->dbuf);
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) {
+ nghttp3_buf_free(&decoder->dbuf, decoder->ctx.mem);
+ nghttp3_qpack_read_state_free(&decoder->rstate);
+ qpack_context_free(&decoder->ctx);
+}
+
+/*
+ * qpack_read_huffman_string decodes huffman string in buffer [begin,
+ * end) and writes the decoded string to |dest|. This function
+ * assumes the buffer pointed by |dest| has enough space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Could not decode huffman string.
+ */
+static nghttp3_ssize qpack_read_huffman_string(nghttp3_qpack_read_state *rstate,
+ nghttp3_buf *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ nghttp3_ssize nwrite;
+ size_t len = (size_t)(end - begin);
+ int fin = 0;
+
+ if (len >= rstate->left) {
+ len = (size_t)rstate->left;
+ fin = 1;
+ }
+
+ nwrite = nghttp3_qpack_huffman_decode(&rstate->huffman_ctx, dest->last, begin,
+ len, fin);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (nghttp3_qpack_huffman_decode_failure_state(&rstate->huffman_ctx)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ dest->last += nwrite;
+ rstate->left -= len;
+ return (nghttp3_ssize)len;
+}
+
+static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate,
+ nghttp3_buf *dest, const uint8_t *begin,
+ const uint8_t *end) {
+ size_t len = (size_t)(end - begin);
+ size_t n = (size_t)nghttp3_min((uint64_t)len, rstate->left);
+
+ dest->last = nghttp3_cpymem(dest->last, begin, n);
+
+ rstate->left -= n;
+ return (nghttp3_ssize)n;
+}
+
+/*
+ * qpack_decoder_validate_index checks rstate->absidx is acceptable.
+ *
+ * It returns 0 if it suceeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * rstate->absidx is invalid.
+ */
+static int qpack_decoder_validate_index(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate) {
+ if (rstate->dynamic) {
+ return rstate->absidx < decoder->ctx.next_absidx &&
+ decoder->ctx.next_absidx - rstate->absidx - 1 <
+ nghttp3_ringbuf_len(&decoder->ctx.dtable)
+ ? 0
+ : NGHTTP3_ERR_QPACK_FATAL;
+ }
+ return rstate->absidx < nghttp3_arraylen(stable) ? 0
+ : NGHTTP3_ERR_QPACK_FATAL;
+}
+
+static void qpack_read_state_check_huffman(nghttp3_qpack_read_state *rstate,
+ const uint8_t b) {
+ rstate->huffman_encoded = (b & (1 << rstate->prefix)) != 0;
+}
+
+static void qpack_read_state_terminate_name(nghttp3_qpack_read_state *rstate) {
+ *rstate->namebuf.last = '\0';
+ rstate->name->len = nghttp3_buf_len(&rstate->namebuf);
+}
+
+static void qpack_read_state_terminate_value(nghttp3_qpack_read_state *rstate) {
+ *rstate->valuebuf.last = '\0';
+ rstate->value->len = nghttp3_buf_len(&rstate->valuebuf);
+}
+
+nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
+ const uint8_t *src,
+ size_t srclen) {
+ const uint8_t *p = src, *end;
+ int rv;
+ int busy = 0;
+ const nghttp3_mem *mem = decoder->ctx.mem;
+ nghttp3_ssize nread;
+ int rfin;
+
+ if (decoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ end = src + srclen;
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (decoder->state) {
+ case NGHTTP3_QPACK_ES_STATE_OPCODE:
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::decode: OPCODE_INSERT_INDEXED\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED;
+ decoder->rstate.dynamic = !((*p) & 0x40);
+ decoder->rstate.prefix = 6;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::decode: OPCODE_INSERT\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT;
+ decoder->rstate.dynamic = 0;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN;
+ } else if ((*p) & 0x20) {
+ DEBUGF("qpack::decode: OPCODE_SET_DTABLE_TABLE_CAP\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else if (!((*p) & 0x20)) {
+ DEBUGF("qpack::decode: OPCODE_DUPLICATE\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_DUPLICATE;
+ decoder->rstate.dynamic = 1;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else {
+ DEBUGF("qpack::decode: unknown opcode %02x\n", *p);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_INDEX:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) {
+ if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) {
+ 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);
+ break;
+ }
+
+ rv = nghttp3_qpack_decoder_rel2abs(decoder, &decoder->rstate);
+ if (rv < 0) {
+ goto fail;
+ }
+
+ if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_DUPLICATE) {
+ rv = nghttp3_qpack_decoder_dtable_duplicate_add(decoder);
+ if (rv != 0) {
+ goto fail;
+ }
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ }
+
+ if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED) {
+ decoder->rstate.prefix = 7;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+ break;
+ }
+
+ /* Unreachable */
+ assert(0);
+ break;
+ case NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN:
+ qpack_read_state_check_huffman(&decoder->rstate, *p);
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAMELEN;
+ decoder->rstate.left = 0;
+ decoder->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_ES_STATE_READ_NAMELEN:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (decoder->rstate.huffman_encoded) {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&decoder->rstate.name,
+ (size_t)decoder->rstate.left * 2 + 1, mem);
+ } else {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME;
+ rv = nghttp3_rcbuf_new(&decoder->rstate.name,
+ (size_t)decoder->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&decoder->rstate.namebuf,
+ decoder->rstate.name->base,
+ decoder->rstate.name->len);
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN:
+ nread = qpack_read_huffman_string(&decoder->rstate,
+ &decoder->rstate.namebuf, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_name(&decoder->rstate);
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+ decoder->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_NAME:
+ nread =
+ qpack_read_string(&decoder->rstate, &decoder->rstate.namebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_name(&decoder->rstate);
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+ decoder->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN:
+ qpack_read_state_check_huffman(&decoder->rstate, *p);
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUELEN;
+ decoder->rstate.left = 0;
+ decoder->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUELEN:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (decoder->rstate.huffman_encoded) {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&decoder->rstate.value,
+ (size_t)decoder->rstate.left * 2 + 1, mem);
+ } else {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE;
+ rv = nghttp3_rcbuf_new(&decoder->rstate.value,
+ (size_t)decoder->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&decoder->rstate.valuebuf,
+ decoder->rstate.value->base,
+ decoder->rstate.value->len);
+
+ /* value might be 0 length */
+ busy = 1;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN:
+ nread = qpack_read_huffman_string(&decoder->rstate,
+ &decoder->rstate.valuebuf, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_value(&decoder->rstate);
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder);
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT:
+ rv = nghttp3_qpack_decoder_dtable_literal_add(decoder);
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUE:
+ nread = qpack_read_string(&decoder->rstate, &decoder->rstate.valuebuf, p,
+ end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_value(&decoder->rstate);
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder);
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT:
+ rv = nghttp3_qpack_decoder_dtable_literal_add(decoder);
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ }
+ }
+
+ return p - src;
+
+fail:
+ decoder->ctx.bad = 1;
+ return rv;
+}
+
+void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder,
+ size_t cap) {
+ nghttp3_qpack_entry *ent;
+ size_t i;
+ nghttp3_qpack_context *ctx = &decoder->ctx;
+ const nghttp3_mem *mem = ctx->mem;
+
+ ctx->max_dtable_size = cap;
+
+ while (ctx->dtable_size > cap) {
+ i = nghttp3_ringbuf_len(&ctx->dtable);
+ assert(i);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
+
+ ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(&ctx->dtable);
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+}
+
+int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) {
+ DEBUGF("qpack::decode: Insert With Name Reference (%s) absidx=%" PRIu64 ": "
+ "value=%*s\n",
+ decoder->rstate.dynamic ? "dynamic" : "static", decoder->rstate.absidx,
+ (int)decoder->rstate.value->len, decoder->rstate.value->base);
+
+ if (decoder->rstate.dynamic) {
+ return nghttp3_qpack_decoder_dtable_dynamic_add(decoder);
+ }
+
+ return nghttp3_qpack_decoder_dtable_static_add(decoder);
+}
+
+int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+ const nghttp3_qpack_static_header *shd;
+
+ shd = &stable[decoder->rstate.absidx];
+
+ if (table_space(shd->name.len, decoder->rstate.value->len) >
+ decoder->ctx.max_dtable_size) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = (nghttp3_rcbuf *)&shd->name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = shd->token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+ nghttp3_qpack_entry *ent;
+
+ 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) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = ent->nv.name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = ent->nv.token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(qnv.name);
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) {
+ int rv;
+ nghttp3_qpack_entry *ent;
+ nghttp3_qpack_nv qnv;
+
+ DEBUGF("qpack::decode: Insert duplicate absidx=%" PRIu64 "\n",
+ decoder->rstate.absidx);
+
+ 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) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv = ent->nv;
+ nghttp3_rcbuf_incref(qnv.name);
+ nghttp3_rcbuf_incref(qnv.value);
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+
+ DEBUGF("qpack::decode: Insert With Literal Name: name=%*s value=%*s\n",
+ (int)decoder->rstate.name->len, decoder->rstate.name->base,
+ (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) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = decoder->rstate.name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = qpack_lookup_token(qnv.name->base, qnv.name->len);
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+void nghttp3_qpack_decoder_set_max_concurrent_streams(
+ nghttp3_qpack_decoder *decoder, size_t max_concurrent_streams) {
+ decoder->max_concurrent_streams =
+ nghttp3_max(decoder->max_concurrent_streams, max_concurrent_streams);
+}
+
+void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ sctx->mem = mem;
+ sctx->rstate.prefix = 8;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_RICNT;
+ sctx->opcode = 0;
+ sctx->stream_id = stream_id;
+ sctx->ricnt = 0;
+ sctx->dbase_sign = 0;
+ sctx->base = 0;
+}
+
+void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state_free(&sctx->rstate);
+}
+
+void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_stream_context_init(sctx, sctx->stream_id, sctx->mem);
+}
+
+uint64_t
+nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx) {
+ return sctx->ricnt;
+}
+
+nghttp3_ssize
+nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv, uint8_t *pflags,
+ const uint8_t *src, size_t srclen, int fin) {
+ const uint8_t *p = src, *end = src ? src + srclen : src;
+ int rv;
+ int busy = 0;
+ nghttp3_ssize nread;
+ int rfin;
+ const nghttp3_mem *mem = decoder->ctx.mem;
+
+ if (decoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ *pflags = NGHTTP3_QPACK_DECODE_FLAG_NONE;
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (sctx->state) {
+ case NGHTTP3_QPACK_RS_STATE_RICNT:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ rv = nghttp3_qpack_decoder_reconstruct_ricnt(decoder, &sctx->ricnt,
+ sctx->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE_SIGN;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_DBASE_SIGN:
+ if ((*p) & 0x80) {
+ sctx->dbase_sign = 1;
+ }
+ sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE;
+ sctx->rstate.left = 0;
+ sctx->rstate.prefix = 7;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_DBASE:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->dbase_sign) {
+ if (sctx->ricnt < sctx->rstate.left) {
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+ sctx->base = sctx->ricnt - sctx->rstate.left - 1;
+ } else {
+ sctx->base = sctx->ricnt + sctx->rstate.left;
+ }
+
+ DEBUGF("qpack::decode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64
+ "\n",
+ sctx->ricnt, sctx->base, decoder->ctx.next_absidx);
+
+ if (sctx->ricnt > decoder->ctx.next_absidx) {
+ DEBUGF("qpack::decode: stream blocked\n");
+ sctx->state = NGHTTP3_QPACK_RS_STATE_BLOCKED;
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED;
+ return p - src;
+ }
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_OPCODE:
+ assert(sctx->rstate.left == 0);
+ assert(sctx->rstate.shift == 0);
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED;
+ sctx->rstate.dynamic = !((*p) & 0x40);
+ sctx->rstate.prefix = 6;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_NAME\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME;
+ sctx->rstate.never = (*p) & 0x20;
+ sctx->rstate.dynamic = !((*p) & 0x10);
+ sctx->rstate.prefix = 4;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else if ((*p) & 0x20) {
+ DEBUGF("qpack::decode: OPCODE_LITERAL\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_LITERAL;
+ sctx->rstate.never = (*p) & 0x10;
+ sctx->rstate.dynamic = 0;
+ sctx->rstate.prefix = 3;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN;
+ } else if ((*p) & 0x10) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_PB\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB;
+ sctx->rstate.dynamic = 1;
+ sctx->rstate.prefix = 4;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_NAME_PB\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB;
+ sctx->rstate.never = (*p) & 0x08;
+ sctx->rstate.dynamic = 1;
+ sctx->rstate.prefix = 3;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ }
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_INDEX:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ switch (sctx->opcode) {
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED:
+ rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv);
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB:
+ rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv);
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
+ rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ sctx->rstate.prefix = 7;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
+ rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ sctx->rstate.prefix = 7;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+ break;
+ case NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN:
+ qpack_read_state_check_huffman(&sctx->rstate, *p);
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAMELEN;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_READ_NAMELEN:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (sctx->rstate.huffman_encoded) {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&sctx->rstate.name,
+ (size_t)sctx->rstate.left * 2 + 1, mem);
+ } else {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME;
+ rv = nghttp3_rcbuf_new(&sctx->rstate.name,
+ (size_t)sctx->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&sctx->rstate.namebuf, sctx->rstate.name->base,
+ sctx->rstate.name->len);
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN:
+ nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.namebuf, p,
+ end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_name(&sctx->rstate);
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ sctx->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_NAME:
+ nread = qpack_read_string(&sctx->rstate, &sctx->rstate.namebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_name(&sctx->rstate);
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ sctx->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN:
+ qpack_read_state_check_huffman(&sctx->rstate, *p);
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUELEN;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUELEN:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (sctx->rstate.huffman_encoded) {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&sctx->rstate.value,
+ (size_t)sctx->rstate.left * 2 + 1, mem);
+ } else {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE;
+ rv = nghttp3_rcbuf_new(&sctx->rstate.value,
+ (size_t)sctx->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&sctx->rstate.valuebuf, sctx->rstate.value->base,
+ sctx->rstate.value->len);
+
+ /* value might be 0 length */
+ busy = 1;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN:
+ nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.valuebuf,
+ p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_value(&sctx->rstate);
+
+ 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);
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
+ nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUE:
+ nread = qpack_read_string(&sctx->rstate, &sctx->rstate.valuebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_value(&sctx->rstate);
+
+ 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);
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
+ nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_STATE_BLOCKED:
+ if (sctx->ricnt > decoder->ctx.next_absidx) {
+ DEBUGF("qpack::decode: stream still blocked\n");
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED;
+ return p - src;
+ }
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+ break;
+ }
+ }
+
+almost_ok:
+ if (fin) {
+ if (sctx->state != NGHTTP3_QPACK_RS_STATE_OPCODE) {
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_FINAL;
+
+ if (sctx->ricnt) {
+ rv = nghttp3_qpack_decoder_write_section_ack(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+ }
+
+ return p - src;
+
+fail:
+ decoder->ctx.bad = 1;
+ return rv;
+}
+
+static int qpack_decoder_dbuf_overflow(nghttp3_qpack_decoder *decoder) {
+ size_t limit = nghttp3_max(decoder->max_concurrent_streams, 100);
+ /* 10 = nghttp3_qpack_put_varint_len((1ULL << 62) - 1, 2)) */
+ return nghttp3_buf_len(&decoder->dbuf) > limit * 2 * 10;
+}
+
+int nghttp3_qpack_decoder_write_section_ack(
+ nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx) {
+ nghttp3_buf *dbuf = &decoder->dbuf;
+ uint8_t *p;
+ int rv;
+
+ if (qpack_decoder_dbuf_overflow(decoder)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = reserve_buf_small(
+ dbuf, nghttp3_qpack_put_varint_len((uint64_t)sctx->stream_id, 7),
+ decoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = dbuf->last;
+ *p = 0x80;
+ dbuf->last = nghttp3_qpack_put_varint(p, (uint64_t)sctx->stream_id, 7);
+
+ if (decoder->written_icnt < sctx->ricnt) {
+ decoder->written_icnt = sctx->ricnt;
+ }
+
+ return 0;
+}
+
+size_t
+nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder) {
+ uint64_t n;
+ size_t len = 0;
+
+ if (decoder->written_icnt < decoder->ctx.next_absidx) {
+ n = decoder->ctx.next_absidx - decoder->written_icnt;
+ len = nghttp3_qpack_put_varint_len(n, 6);
+ }
+
+ return nghttp3_buf_len(&decoder->dbuf) + len;
+}
+
+void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
+ nghttp3_buf *dbuf) {
+ uint8_t *p;
+ uint64_t n = 0;
+ size_t len = 0;
+
+ if (decoder->written_icnt < decoder->ctx.next_absidx) {
+ n = decoder->ctx.next_absidx - decoder->written_icnt;
+ len = nghttp3_qpack_put_varint_len(n, 6);
+ }
+
+ assert(nghttp3_buf_left(dbuf) >= nghttp3_buf_len(&decoder->dbuf) + len);
+
+ if (nghttp3_buf_len(&decoder->dbuf)) {
+ dbuf->last = nghttp3_cpymem(dbuf->last, decoder->dbuf.pos,
+ nghttp3_buf_len(&decoder->dbuf));
+ }
+
+ if (n) {
+ p = dbuf->last;
+ *p = 0;
+ dbuf->last = nghttp3_qpack_put_varint(p, n, 6);
+
+ decoder->written_icnt = decoder->ctx.next_absidx;
+ }
+
+ nghttp3_buf_reset(&decoder->dbuf);
+}
+
+int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
+ int64_t stream_id) {
+ uint8_t *p;
+ int rv;
+
+ if (qpack_decoder_dbuf_overflow(decoder)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = reserve_buf(&decoder->dbuf,
+ nghttp3_qpack_put_varint_len((uint64_t)stream_id, 6),
+ decoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = decoder->dbuf.last;
+ *p = 0x40;
+ decoder->dbuf.last = nghttp3_qpack_put_varint(p, (uint64_t)stream_id, 6);
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
+ uint64_t *dest, uint64_t encricnt) {
+ uint64_t max_ents, full, max, max_wrapped, ricnt;
+
+ if (encricnt == 0) {
+ *dest = 0;
+ return 0;
+ }
+
+ max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ full = 2 * max_ents;
+
+ if (encricnt > full) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ max = decoder->ctx.next_absidx + max_ents;
+ max_wrapped = max / full * full;
+ ricnt = max_wrapped + encricnt - 1;
+
+ if (ricnt > max) {
+ if (ricnt <= full) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ ricnt -= full;
+ }
+
+ if (ricnt == 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ *dest = ricnt;
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate) {
+ DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " icnt=%" PRIu64 "\n",
+ rstate->dynamic, rstate->left, decoder->ctx.next_absidx);
+
+ if (rstate->dynamic) {
+ if (decoder->ctx.next_absidx < rstate->left + 1) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+ rstate->absidx = decoder->ctx.next_absidx - rstate->left - 1;
+ } else {
+ rstate->absidx = rstate->left;
+ }
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+ return 0;
+}
+
+int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state *rstate = &sctx->rstate;
+
+ DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " base=%" PRIu64
+ " icnt=%" PRIu64 "\n",
+ rstate->dynamic, rstate->left, sctx->base, decoder->ctx.next_absidx);
+
+ if (rstate->dynamic) {
+ if (sctx->base < rstate->left + 1) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ rstate->absidx = sctx->base - rstate->left - 1;
+
+ if (rstate->absidx >= sctx->ricnt) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ } else {
+ rstate->absidx = rstate->left;
+ }
+
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ return 0;
+}
+
+int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state *rstate = &sctx->rstate;
+
+ DEBUGF("qpack::decode: pbidx=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n",
+ rstate->left, sctx->base, decoder->ctx.next_absidx);
+
+ assert(rstate->dynamic);
+
+ rstate->absidx = rstate->left + sctx->base;
+
+ if (rstate->absidx >= sctx->ricnt) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ return 0;
+}
+
+static void
+qpack_decoder_emit_static_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx];
+ (void)decoder;
+
+ nv->name = (nghttp3_rcbuf *)&shd->name;
+ nv->value = (nghttp3_rcbuf *)&shd->value;
+ nv->token = shd->token;
+ nv->flags = NGHTTP3_NV_FLAG_NONE;
+}
+
+static void
+qpack_decoder_emit_dynamic_indexed(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);
+
+ *nv = ent->nv;
+
+ nghttp3_rcbuf_incref(nv->name);
+ nghttp3_rcbuf_incref(nv->value);
+}
+
+void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ DEBUGF("qpack::decode: Indexed (%s) absidx=%" PRIu64 "\n",
+ sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx);
+
+ if (sctx->rstate.dynamic) {
+ qpack_decoder_emit_dynamic_indexed(decoder, sctx, nv);
+ } else {
+ qpack_decoder_emit_static_indexed(decoder, sctx, nv);
+ }
+}
+
+static void
+qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx];
+ (void)decoder;
+
+ nv->name = (nghttp3_rcbuf *)&shd->name;
+ nv->value = sctx->rstate.value;
+ nv->token = shd->token;
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ sctx->rstate.value = NULL;
+}
+
+static void
+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;
+
+ nv->name = ent->nv.name;
+ nv->value = sctx->rstate.value;
+ nv->token = ent->nv.token;
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(nv->name);
+
+ sctx->rstate.value = NULL;
+}
+
+void 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",
+ sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx,
+ (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);
+ }
+}
+
+void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ (void)decoder;
+
+ DEBUGF("qpack::decode: Emit literal name=%*s value=%*s\n",
+ (int)sctx->rstate.name->len, sctx->rstate.name->base,
+ (int)sctx->rstate.value->len, sctx->rstate.value->base);
+
+ nv->name = sctx->rstate.name;
+ nv->value = sctx->rstate.value;
+ nv->token = qpack_lookup_token(nv->name->base, nv->name->len);
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ sctx->rstate.name = NULL;
+ sctx->rstate.value = NULL;
+}
+
+int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
+ size_t max_dtable_size, size_t max_blocked,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_encoder *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *pencoder = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) {
+ const nghttp3_mem *mem;
+
+ if (encoder == NULL) {
+ return;
+ }
+
+ mem = encoder->ctx.mem;
+
+ nghttp3_qpack_encoder_free(encoder);
+ nghttp3_mem_free(mem, encoder);
+}
+
+int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_stream_context *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_qpack_stream_context_init(p, stream_id, mem);
+
+ *psctx = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) {
+ const nghttp3_mem *mem;
+
+ if (sctx == NULL) {
+ return;
+ }
+
+ mem = sctx->mem;
+
+ nghttp3_qpack_stream_context_free(sctx);
+ nghttp3_mem_free(mem, sctx);
+}
+
+int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
+ size_t max_dtable_size, size_t max_blocked,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_decoder *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *pdecoder = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder) {
+ const nghttp3_mem *mem;
+
+ if (decoder == NULL) {
+ return;
+ }
+
+ mem = decoder->ctx.mem;
+
+ nghttp3_qpack_decoder_free(decoder);
+ nghttp3_mem_free(mem, decoder);
+}
+
+uint64_t nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder) {
+ return decoder->ctx.next_absidx;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h
new file mode 100644
index 0000000000..429c55a780
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h
@@ -0,0 +1,961 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_QPACK_H
+#define NGHTTP3_QPACK_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_rcbuf.h"
+#include "nghttp3_map.h"
+#include "nghttp3_pq.h"
+#include "nghttp3_ringbuf.h"
+#include "nghttp3_buf.h"
+#include "nghttp3_ksl.h"
+#include "nghttp3_qpack_huffman.h"
+
+#define NGHTTP3_QPACK_INT_MAX ((1ull << 62) - 1)
+
+/* NGHTTP3_QPACK_MAX_NAMELEN is the maximum (compressed) length of
+ header name this library can decode. */
+#define NGHTTP3_QPACK_MAX_NAMELEN 256
+/* NGHTTP3_QPACK_MAX_VALUELEN is the maximum (compressed) length of
+ header value this library can decode. */
+#define NGHTTP3_QPACK_MAX_VALUELEN 65536
+
+/* nghttp3_qpack_indexing_mode is a indexing strategy. */
+typedef enum nghttp3_qpack_indexing_mode {
+ /* NGHTTP3_QPACK_INDEXING_MODE_LITERAL means that header field
+ should not be inserted into dynamic table. */
+ NGHTTP3_QPACK_INDEXING_MODE_LITERAL,
+ /* NGHTTP3_QPACK_INDEXING_MODE_STORE means that header field can be
+ inserted into dynamic table. */
+ NGHTTP3_QPACK_INDEXING_MODE_STORE,
+ /* NGHTTP3_QPACK_INDEXING_MODE_NEVER means that header field should
+ not be inserted into dynamic table and this must be true for all
+ forwarding paths. */
+ NGHTTP3_QPACK_INDEXING_MODE_NEVER,
+} nghttp3_qpack_indexing_mode;
+
+typedef struct nghttp3_qpack_entry nghttp3_qpack_entry;
+
+struct nghttp3_qpack_entry {
+ /* The header field name/value pair */
+ nghttp3_qpack_nv nv;
+ /* map_next points to the entry which shares same bucket in hash
+ table. */
+ nghttp3_qpack_entry *map_next;
+ /* sum is the sum of all entries inserted up to this entry. This
+ value does not contain the space required for this entry. */
+ size_t sum;
+ /* absidx is the absolute index of this entry. */
+ uint64_t absidx;
+ /* The hash value for header name (nv.name). */
+ uint32_t hash;
+};
+
+/* The entry used for static table. */
+typedef struct nghttp3_qpack_static_entry {
+ uint64_t absidx;
+ int32_t token;
+ uint32_t hash;
+} nghttp3_qpack_static_entry;
+
+typedef struct nghttp3_qpack_static_header {
+ nghttp3_rcbuf name;
+ nghttp3_rcbuf value;
+ int32_t token;
+} nghttp3_qpack_static_header;
+
+/*
+ * nghttp3_qpack_header_block_ref is created per encoded header block
+ * and includes the required insert count and the minimum insert count
+ * of dynamic table entry it refers to.
+ */
+typedef struct nghttp3_qpack_header_block_ref {
+ nghttp3_pq_entry max_cnts_pe;
+ nghttp3_pq_entry min_cnts_pe;
+ /* max_cnt is the required insert count. */
+ uint64_t max_cnt;
+ /* min_cnt is the minimum insert count of dynamic table entry it
+ refers to. In other words, this is the minimum absolute index of
+ dynamic header table entry this encoded block refers to plus
+ 1. */
+ uint64_t min_cnt;
+} nghttp3_qpack_header_block_ref;
+
+int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref,
+ uint64_t max_cnt, uint64_t min_cnt,
+ const nghttp3_mem *mem);
+
+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;
+ /* 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
+ headers, trailers, and push promises) per stream. */
+ nghttp3_ringbuf refs;
+ /* max_cnts is a priority queue sorted by descending order of
+ max_cnt of nghttp3_qpack_header_block_ref. */
+ nghttp3_pq max_cnts;
+} nghttp3_qpack_stream;
+
+int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
+ const nghttp3_mem *mem);
+
+void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream,
+ const nghttp3_mem *mem);
+
+uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream);
+
+int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream,
+ nghttp3_qpack_header_block_ref *ref);
+
+void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream);
+
+#define NGHTTP3_QPACK_ENTRY_OVERHEAD 32
+
+typedef struct nghttp3_qpack_context {
+ /* dtable is a dynamic table */
+ nghttp3_ringbuf dtable;
+ /* mem is memory allocator */
+ const nghttp3_mem *mem;
+ /* dtable_size is abstracted buffer size of dtable as described in
+ the spec. This is the sum of length of name/value in dtable +
+ 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
+ blocked. */
+ size_t max_blocked;
+ /* next_absidx is the next absolute index for nghttp3_qpack_entry.
+ It is equivalent to insert count. */
+ uint64_t next_absidx;
+ /* If inflate/deflate error occurred, this value is set to 1 and
+ further invocation of inflate/deflate will fail with
+ NGHTTP3_ERR_QPACK_FATAL. */
+ uint8_t bad;
+} nghttp3_qpack_context;
+
+typedef struct nghttp3_qpack_read_state {
+ nghttp3_qpack_huffman_decode_context huffman_ctx;
+ nghttp3_buf namebuf;
+ nghttp3_buf valuebuf;
+ nghttp3_rcbuf *name;
+ nghttp3_rcbuf *value;
+ uint64_t left;
+ size_t prefix;
+ size_t shift;
+ uint64_t absidx;
+ int never;
+ int dynamic;
+ int huffman_encoded;
+} nghttp3_qpack_read_state;
+
+void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate);
+
+void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate);
+
+#define NGHTTP3_QPACK_MAP_SIZE 64
+
+typedef struct nghttp3_qpack_map {
+ nghttp3_qpack_entry *table[NGHTTP3_QPACK_MAP_SIZE];
+} nghttp3_qpack_map;
+
+/* nghttp3_qpack_decoder_stream_state is a set of states when decoding
+ decoder stream. */
+typedef enum nghttp3_qpack_decoder_stream_state {
+ NGHTTP3_QPACK_DS_STATE_OPCODE,
+ NGHTTP3_QPACK_DS_STATE_READ_NUMBER,
+} nghttp3_qpack_decoder_stream_state;
+
+/* nghttp3_qpack_decoder_stream_opcode is opcode used in decoder
+ stream. */
+typedef enum nghttp3_qpack_decoder_stream_opcode {
+ NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT,
+ NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK,
+ NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL,
+} 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
+/* 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
+
+struct nghttp3_qpack_encoder {
+ nghttp3_qpack_context ctx;
+ /* dtable_map is a map of hash to nghttp3_qpack_entry to provide
+ fast access to an entry in dynamic table. */
+ nghttp3_qpack_map dtable_map;
+ /* streams is a map of stream ID to nghttp3_qpack_stream to keep
+ track of unacknowledged streams. */
+ nghttp3_map streams;
+ /* blocked_streams is an ordered list of nghttp3_qpack_stream, in
+ descending order of max_cnt, to search the unblocked streams by
+ received known count. */
+ nghttp3_ksl blocked_streams;
+ /* min_cnts is a priority queue of nghttp3_qpack_header_block_ref
+ sorted by ascending order of min_cnt to know that an entry can be
+ evicted from dynamic table. */
+ nghttp3_pq min_cnts;
+ /* krcnt is Known Received Count. */
+ uint64_t krcnt;
+ /* state is a current state of reading decoder stream. */
+ nghttp3_qpack_decoder_stream_state state;
+ /* opcode is a decoder stream opcode being processed. */
+ nghttp3_qpack_decoder_stream_opcode opcode;
+ /* rstate is a set of intermediate state which are used to process
+ decoder stream. */
+ nghttp3_qpack_read_state rstate;
+ /* min_dtable_update is the minimum dynamic table size required. */
+ size_t min_dtable_update;
+ /* last_max_dtable_update is the dynamic table size last
+ requested. */
+ size_t last_max_dtable_update;
+ /* flags is bitwise OR of zero or more of
+ NGHTTP3_QPACK_ENCODER_FLAG_*. */
+ uint8_t flags;
+};
+
+/*
+ * 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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
+ size_t max_dtable_size, size_t max_blocked,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_encoder_free frees memory allocated for |encoder|.
+ * This function does not free memory pointed by |encoder|.
+ */
+void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_encode_nv encodes |nv|. It writes request
+ * stream into |rbuf| and writes encoder stream into |ebuf|. |nv| is
+ * a header field to encode. |base| is base. |allow_blocking| is
+ * nonzero if this stream can be blocked (or it has been blocked
+ * already).
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
+ uint64_t *pmax_cnt, uint64_t *pmin_cnt,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ const nghttp3_nv *nv, uint64_t base,
+ int allow_blocking);
+
+/* nghttp3_qpack_lookup_result stores a result of table lookup. */
+typedef struct nghttp3_qpack_lookup_result {
+ /* index is an index of matched entry. -1 if no match is made. */
+ nghttp3_ssize index;
+ /* name_value_match is nonzero if both name and value are
+ matched. */
+ int name_value_match;
+ /* pb_index is the absolute index of matched post-based dynamic
+ table entry. -1 if no such entry exists. */
+ nghttp3_ssize pb_index;
+} nghttp3_qpack_lookup_result;
+
+/*
+ * nghttp3_qpack_lookup_stable searches |nv| in static table. |token|
+ * is a token of nv->name and it is -1 if there is no corresponding
+ * token defined. |indexing_mode| provides indexing strategy.
+ */
+nghttp3_qpack_lookup_result
+nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token,
+ nghttp3_qpack_indexing_mode indexing_mode);
+
+/*
+ * nghttp3_qpack_encoder_lookup_dtable searches |nv| in dynamic table.
+ * |token| is a token of nv->name and it is -1 if there is no
+ * corresponding token defined. |hash| is a hash of nv->name.
+ * |indexing_mode| provides indexing strategy. |krcnt| is Known
+ * Received Count. |allow_blocking| is nonzero if this stream can be
+ * blocked (or it has been blocked already).
+ */
+nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable(
+ nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt,
+ int allow_blocking);
+
+/*
+ * nghttp3_qpack_encoder_write_field_section_prefix writes Encoded
+ * Field Section Prefix into |pbuf|. |ricnt| is Required Insert
+ * Count. |base| is Base.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_field_section_prefix(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt,
+ uint64_t base);
+
+/*
+ * nghttp3_qpack_encoder_write_static_indexed writes Indexed Header
+ * Field to |rbuf|. |absidx| is an absolute index into static table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_indexed writes Indexed Header
+ * Field to |rbuf|. |absidx| is an absolute index into dynamic table.
+ * |base| is base.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx, uint64_t base);
+
+/*
+ * nghttp3_qpack_encoder_write_static_indexed writes Literal Header
+ * Field With Name Reference to |rbuf|. |absidx| is an absolute index
+ * into static table to reference a name. |nv| is a header field to
+ * encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_indexed writes Literal Header
+ * Field With Name Reference to |rbuf|. |absidx| is an absolute index
+ * into dynamic table to reference a name. |base| is a base. |nv| is
+ * a header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ uint64_t base, const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_literal writes Literal Header Field
+ * With Literal Name to |rbuf|. |nv| is a header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_static_insert writes Insert With Name
+ * Reference to |ebuf|. |absidx| is an absolute index into static
+ * table to reference a name. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_insert writes Insert With Name
+ * Reference to |ebuf|. |absidx| is an absolute index into dynamic
+ * table to reference a name. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_duplicate_insert writes Duplicate to
+ * |ebuf|. |absidx| is an absolute index into dynamic table to
+ * reference an entry.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_write_literal_insert writes Insert With
+ * Literal Name to |ebuf|. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ const nghttp3_nv *nv);
+
+int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_block_stream blocks |stream|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_unblock_stream unblocks |stream|.
+ */
+void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_unblock unblocks stream whose max_cnt is less
+ * than or equal to |max_cnt|.
+ */
+void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
+ uint64_t max_cnt);
+
+/*
+ * nghttp3_qpack_encoder_find_stream returns stream whose stream ID is
+ * |stream_id|. This function returns NULL if there is no such
+ * stream.
+ */
+nghttp3_qpack_stream *
+nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_shrink_dtable shrinks dynamic table so that
+ * the dynamic table size is less than or equal to maximum size.
+ */
+void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_process_dtable_update processes pending
+ * dynamic table size update. It might write encoder stream into
+ * |ebuf|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf);
+
+/*
+ * nghttp3_qpack_encoder_write_set_dtable_cap writes Set Dynamic Table
+ * Capacity. to |ebuf|. |cap| is the capacity of dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf, size_t cap);
+
+/*
+ * nghttp3_qpack_context_dtable_add adds |qnv| to dynamic table. If
+ * |ctx| is a part of encoder, |dtable_map| is not NULL. |hash| is a
+ * hash value of name.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
+ nghttp3_qpack_nv *qnv,
+ nghttp3_qpack_map *dtable_map,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_static_add adds |nv| to dynamic table
+ * by referencing static table entry at an absolute index |absidx|.
+ * The hash of name is given as |hash|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_dynamic_add adds |nv| to dynamic table
+ * by referencing dynamic table entry at an absolute index |absidx|.
+ * The hash of name is given as |hash|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_duplicate_add duplicates dynamic table
+ * entry at an absolute index |absidx|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_dtable_literal_add adds |nv| to dynamic
+ * table. |token| is a token of name and is -1 if it has no token
+ * value defined. |hash| is a hash of name.
+ *
+ * NGHTTP3_ERR_NOMEM Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv,
+ int32_t token, uint32_t hash);
+
+/*
+ * nghttp3_qpack_context_dtable_get returns dynamic table entry whose
+ * absolute index is |absidx|. This function assumes that such entry
+ * exists.
+ */
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx);
+
+/*
+ * nghttp3_qpack_context_dtable_top returns latest dynamic table
+ * entry. This function assumes dynamic table is not empty.
+ */
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx);
+
+/*
+ * nghttp3_qpack_entry_init initializes |ent|. |qnv| is a header
+ * field. |sum| is the sum of table space occupied by all entries
+ * inserted so far. It does not include this entry. |absidx| is an
+ * absolute index of this entry. |hash| is a hash of header field
+ * name. This function increases reference count of qnv->nv.name and
+ * qnv->nv.value.
+ */
+void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv,
+ size_t sum, uint64_t absidx, uint32_t hash);
+
+/*
+ * nghttp3_qpack_entry_free frees memory allocated for |ent|.
+ */
+void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent);
+
+/*
+ * nghttp3_qpack_put_varint_len returns the required number of bytes
+ * to encode |n| with |prefix| bits.
+ */
+size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix);
+
+/*
+ * nghttp3_qpack_put_varint encodes |n| using variable integer
+ * encoding with |prefix| bits into |buf|. This function assumes the
+ * buffer pointed by |buf| has enough space. This function returns
+ * the one byte beyond the last write (buf +
+ * nghttp3_qpack_put_varint_len(n, prefix)).
+ */
+uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix);
+
+/* nghttp3_qpack_encoder_stream_state is a set of states for encoder
+ stream decoding. */
+typedef enum nghttp3_qpack_encoder_stream_state {
+ NGHTTP3_QPACK_ES_STATE_OPCODE,
+ NGHTTP3_QPACK_ES_STATE_READ_INDEX,
+ NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAMELEN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAME,
+ NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUELEN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUE,
+} nghttp3_qpack_encoder_stream_state;
+
+/* nghttp3_qpack_encoder_stream_opcode is a set of opcodes used in
+ encoder stream. */
+typedef enum nghttp3_qpack_encoder_stream_opcode {
+ NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED,
+ NGHTTP3_QPACK_ES_OPCODE_INSERT,
+ NGHTTP3_QPACK_ES_OPCODE_DUPLICATE,
+ NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP,
+} nghttp3_qpack_encoder_stream_opcode;
+
+/* nghttp3_qpack_request_stream_state is a set of states for request
+ stream decoding. */
+typedef enum nghttp3_qpack_request_stream_state {
+ NGHTTP3_QPACK_RS_STATE_RICNT,
+ NGHTTP3_QPACK_RS_STATE_DBASE_SIGN,
+ NGHTTP3_QPACK_RS_STATE_DBASE,
+ NGHTTP3_QPACK_RS_STATE_OPCODE,
+ NGHTTP3_QPACK_RS_STATE_READ_INDEX,
+ NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAMELEN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAME,
+ NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUELEN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUE,
+ NGHTTP3_QPACK_RS_STATE_BLOCKED,
+} nghttp3_qpack_request_stream_state;
+
+/* nghttp3_qpack_request_stream_opcode is a set of opcodes used in
+ request stream. */
+typedef enum nghttp3_qpack_request_stream_opcode {
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB,
+ NGHTTP3_QPACK_RS_OPCODE_LITERAL,
+} nghttp3_qpack_request_stream_opcode;
+
+struct nghttp3_qpack_decoder {
+ nghttp3_qpack_context ctx;
+ /* state is a current state of reading encoder stream. */
+ nghttp3_qpack_encoder_stream_state state;
+ /* opcode is an encoder stream opcode being processed. */
+ nghttp3_qpack_encoder_stream_opcode opcode;
+ /* rstate is a set of intermediate state which are used to process
+ encoder stream. */
+ nghttp3_qpack_read_state rstate;
+ /* dbuf is decoder stream. */
+ nghttp3_buf dbuf;
+ /* written_icnt is Insert Count written to decoder stream so far. */
+ uint64_t written_icnt;
+ /* max_concurrent_streams is the number of concurrent streams that a
+ remote endpoint can open, including both bidirectional and
+ unidirectional streams which potentially receives QPACK encoded
+ HEADER frame. */
+ size_t max_concurrent_streams;
+};
+
+/*
+ * 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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
+ size_t max_dtable_size, size_t max_blocked,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_decoder_free frees memory allocated for |decoder|.
+ * This function does not free memory pointed by |decoder|.
+ */
+void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_indexed_add adds entry received in
+ * Insert With Name Reference to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_static_add adds entry received in
+ * Insert With Name Reference (static) to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_dynamic_add adds entry received in
+ * Insert With Name Reference (dynamic) to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_duplicate_add adds entry received in
+ * Duplicate to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_literal_add adds entry received in
+ * Insert With Literal Name to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder);
+
+struct nghttp3_qpack_stream_context {
+ /* state is a current state of reading request stream. */
+ nghttp3_qpack_request_stream_state state;
+ /* rstate is a set of intermediate state which are used to process
+ request stream. */
+ nghttp3_qpack_read_state rstate;
+ const nghttp3_mem *mem;
+ /* opcode is a request stream opcode being processed. */
+ nghttp3_qpack_request_stream_opcode opcode;
+ int64_t stream_id;
+ /* ricnt is Required Insert Count to decode this header block. */
+ uint64_t ricnt;
+ /* base is Base in Header Block Prefix. */
+ uint64_t base;
+ /* dbase_sign is the delta base sign in Header Block Prefix. */
+ int dbase_sign;
+};
+
+/*
+ * nghttp3_qpack_stream_context_init initializes |sctx|.
+ */
+void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_stream_context_free frees memory allocated for
+ * |sctx|. This function does not free memory pointed by |sctx|.
+ */
+void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx);
+
+/*
+ * nghttp3_qpack_decoder_reconstruct_ricnt reconstructs Required
+ * Insert Count from the encoded form |encricnt| and stores Required
+ * Insert Count in |*dest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Unable to reconstruct Required Insert Count.
+ */
+int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
+ uint64_t *dest, uint64_t encricnt);
+
+/*
+ * nghttp3_qpack_decoder_rel2abs converts relative index rstate->left
+ * received in encoder stream to absolute index and stores it in
+ * rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Relative index is invalid.
+ */
+int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate);
+
+/*
+ * nghttp3_qpack_decoder_brel2abs converts Base relative index
+ * rstate->left received in request stream to absolute index and
+ * stores it in rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Base relative index is invalid.
+ */
+int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx);
+
+/*
+ * nghttp3_qpack_decoder_pbrel2abs converts Post-Base relative index
+ * rstate->left received in request stream to absolute index and
+ * stores it in rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Post-Base relative index is invalid.
+ */
+int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx);
+
+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);
+
+void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
+
+/*
+ * nghttp3_qpack_decoder_write_section_ack writes Section
+ * Acknowledgement to decoder stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Decoder stream overflow.
+ */
+int nghttp3_qpack_decoder_write_section_ack(
+ nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx);
+
+#endif /* NGHTTP3_QPACK_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c
new file mode 100644
index 0000000000..c36a68eded
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c
@@ -0,0 +1,122 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack_huffman.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+
+size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len) {
+ size_t i;
+ size_t nbits = 0;
+
+ for (i = 0; i < len; ++i) {
+ nbits += huffman_sym_table[src[i]].nbits;
+ }
+ /* pad the prefix of EOS (256) */
+ return (nbits + 7) / 8;
+}
+
+uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src,
+ size_t srclen) {
+ const nghttp3_qpack_huffman_sym *sym;
+ const uint8_t *end = src + srclen;
+ uint64_t code = 0;
+ size_t nbits = 0;
+ uint32_t x;
+
+ for (; src != end;) {
+ sym = &huffman_sym_table[*src++];
+ code |= (uint64_t)sym->code << (32 - nbits);
+ nbits += sym->nbits;
+ if (nbits < 32) {
+ continue;
+ }
+ x = htonl((uint32_t)(code >> 32));
+ memcpy(dest, &x, 4);
+ dest += 4;
+ code <<= 32;
+ nbits -= 32;
+ }
+
+ for (; nbits >= 8;) {
+ *dest++ = (uint8_t)(code >> 56);
+ code <<= 8;
+ nbits -= 8;
+ }
+
+ if (nbits) {
+ *dest++ = (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1));
+ }
+
+ return dest;
+}
+
+void nghttp3_qpack_huffman_decode_context_init(
+ nghttp3_qpack_huffman_decode_context *ctx) {
+ ctx->fstate = NGHTTP3_QPACK_HUFFMAN_ACCEPTED;
+}
+
+nghttp3_ssize
+nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx,
+ uint8_t *dest, const uint8_t *src, size_t srclen,
+ int fin) {
+ uint8_t *p = dest;
+ const uint8_t *end = src + srclen;
+ nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0};
+ const nghttp3_qpack_huffman_decode_node *t = &node;
+ uint8_t c;
+
+ /* We use the decoding algorithm described in
+ http://graphics.ics.uci.edu/pub/Prefix.pdf */
+ for (; src != end;) {
+ c = *src++;
+ t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c >> 4];
+ if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) {
+ *p++ = t->sym;
+ }
+
+ t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c & 0xf];
+ if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) {
+ *p++ = t->sym;
+ }
+ }
+
+ ctx->fstate = t->fstate;
+
+ if (fin && !(ctx->fstate & NGHTTP3_QPACK_HUFFMAN_ACCEPTED)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ return p - dest;
+}
+
+int nghttp3_qpack_huffman_decode_failure_state(
+ nghttp3_qpack_huffman_decode_context *ctx) {
+ return ctx->fstate == 0x100;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h
new file mode 100644
index 0000000000..fc3bc7b264
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h
@@ -0,0 +1,108 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_QPACK_HUFFMAN_H
+#define NGHTTP3_QPACK_HUFFMAN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_qpack_huffman_sym {
+ /* The number of bits in this code */
+ uint32_t nbits;
+ /* Huffman code aligned to LSB */
+ uint32_t code;
+} nghttp3_qpack_huffman_sym;
+
+extern const nghttp3_qpack_huffman_sym huffman_sym_table[];
+
+size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len);
+
+uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src,
+ size_t srclen);
+
+typedef enum nghttp3_qpack_huffman_decode_flag {
+ /* FSA accepts this state as the end of huffman encoding
+ sequence. */
+ NGHTTP3_QPACK_HUFFMAN_ACCEPTED = 1 << 14,
+ /* This state emits symbol */
+ NGHTTP3_QPACK_HUFFMAN_SYM = 1 << 15,
+} nghttp3_qpack_huffman_decode_flag;
+
+typedef struct nghttp3_qpack_huffman_decode_node {
+ /* fstate is the current huffman decoding state, which is actually
+ the node ID of internal huffman tree with
+ nghttp3_qpack_huffman_decode_flag OR-ed. We have 257 leaf nodes,
+ but they are identical to root node other than emitting a symbol,
+ so we have 256 internal nodes [1..256], inclusive. The node ID
+ 256 is a special node and it is a terminal state that means
+ decoding failed. */
+ uint16_t fstate;
+ /* symbol if NGHTTP3_QPACK_HUFFMAN_SYM flag set */
+ uint8_t sym;
+} nghttp3_qpack_huffman_decode_node;
+
+typedef struct nghttp3_qpack_huffman_decode_context {
+ /* fstate is the current huffman decoding state. */
+ uint16_t fstate;
+} nghttp3_qpack_huffman_decode_context;
+
+extern const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16];
+
+void nghttp3_qpack_huffman_decode_context_init(
+ nghttp3_qpack_huffman_decode_context *ctx);
+
+/*
+ * nghttp3_qpack_huffman_decode decodes huffman encoded byte string
+ * stored in |src| of length |srclen|. |ctx| is a decoding context.
+ * |ctx| remembers the decoding state, and caller can call this
+ * function multiple times to feed each chunk of huffman encoded
+ * substring. |fin| must be nonzero if |src| contains the last chunk
+ * of huffman string. The decoded string is written to the buffer
+ * pointed by |dest|. This function assumes that the buffer pointed
+ * by |dest| contains enough memory to store decoded byte string.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Could not decode huffman string.
+ */
+nghttp3_ssize
+nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx,
+ uint8_t *dest, const uint8_t *src, size_t srclen,
+ int fin);
+
+/*
+ * nghttp3_qpack_huffman_decode_failure_state returns nonzero if |ctx|
+ * indicates that huffman decoding context is in failure state.
+ */
+int nghttp3_qpack_huffman_decode_failure_state(
+ nghttp3_qpack_huffman_decode_context *ctx);
+
+#endif /* NGHTTP3_QPACK_HUFFMAN_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c
new file mode 100644
index 0000000000..0c104dbc0a
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c
@@ -0,0 +1,4981 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack_huffman.h"
+
+/* Generated by mkhufftbl.py */
+
+const nghttp3_qpack_huffman_sym huffman_sym_table[] = {
+ {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u},
+ {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u},
+ {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u},
+ {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u},
+ {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u},
+ {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u},
+ {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u},
+ {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u},
+ {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u},
+ {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u},
+ {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u},
+ {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u},
+ {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u},
+ {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u},
+ {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u},
+ {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u},
+ {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u},
+ {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u},
+ {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u},
+ {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u},
+ {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u},
+ {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u},
+ {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u},
+ {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u},
+ {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u},
+ {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u},
+ {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u},
+ {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u},
+ {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u},
+ {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u},
+ {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u},
+ {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u},
+ {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u},
+ {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u},
+ {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u},
+ {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u},
+ {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u},
+ {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u},
+ {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u},
+ {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u},
+ {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u},
+ {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u},
+ {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u},
+ {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u},
+ {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u},
+ {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u},
+ {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u},
+ {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u},
+ {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u},
+ {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u},
+ {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u},
+ {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u},
+ {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u},
+ {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u},
+ {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u},
+ {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u},
+ {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u},
+ {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u},
+ {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u},
+ {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u},
+ {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u},
+ {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u},
+ {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u},
+ {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u},
+ {30, 0xfffffffcu}};
+
+const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16] = {
+ /* 0 */
+ {
+ {0x04, 0},
+ {0x05, 0},
+ {0x07, 0},
+ {0x08, 0},
+ {0x0b, 0},
+ {0x0c, 0},
+ {0x10, 0},
+ {0x13, 0},
+ {0x19, 0},
+ {0x1c, 0},
+ {0x20, 0},
+ {0x23, 0},
+ {0x2a, 0},
+ {0x31, 0},
+ {0x39, 0},
+ {0x4040, 0},
+ },
+ /* 1 */
+ {
+ {0xc000, 48},
+ {0xc000, 49},
+ {0xc000, 50},
+ {0xc000, 97},
+ {0xc000, 99},
+ {0xc000, 101},
+ {0xc000, 105},
+ {0xc000, 111},
+ {0xc000, 115},
+ {0xc000, 116},
+ {0x0d, 0},
+ {0x0e, 0},
+ {0x11, 0},
+ {0x12, 0},
+ {0x14, 0},
+ {0x15, 0},
+ },
+ /* 2 */
+ {
+ {0x8001, 48},
+ {0xc016, 48},
+ {0x8001, 49},
+ {0xc016, 49},
+ {0x8001, 50},
+ {0xc016, 50},
+ {0x8001, 97},
+ {0xc016, 97},
+ {0x8001, 99},
+ {0xc016, 99},
+ {0x8001, 101},
+ {0xc016, 101},
+ {0x8001, 105},
+ {0xc016, 105},
+ {0x8001, 111},
+ {0xc016, 111},
+ },
+ /* 3 */
+ {
+ {0x8002, 48},
+ {0x8009, 48},
+ {0x8017, 48},
+ {0xc028, 48},
+ {0x8002, 49},
+ {0x8009, 49},
+ {0x8017, 49},
+ {0xc028, 49},
+ {0x8002, 50},
+ {0x8009, 50},
+ {0x8017, 50},
+ {0xc028, 50},
+ {0x8002, 97},
+ {0x8009, 97},
+ {0x8017, 97},
+ {0xc028, 97},
+ },
+ /* 4 */
+ {
+ {0x8003, 48},
+ {0x8006, 48},
+ {0x800a, 48},
+ {0x800f, 48},
+ {0x8018, 48},
+ {0x801f, 48},
+ {0x8029, 48},
+ {0xc038, 48},
+ {0x8003, 49},
+ {0x8006, 49},
+ {0x800a, 49},
+ {0x800f, 49},
+ {0x8018, 49},
+ {0x801f, 49},
+ {0x8029, 49},
+ {0xc038, 49},
+ },
+ /* 5 */
+ {
+ {0x8003, 50},
+ {0x8006, 50},
+ {0x800a, 50},
+ {0x800f, 50},
+ {0x8018, 50},
+ {0x801f, 50},
+ {0x8029, 50},
+ {0xc038, 50},
+ {0x8003, 97},
+ {0x8006, 97},
+ {0x800a, 97},
+ {0x800f, 97},
+ {0x8018, 97},
+ {0x801f, 97},
+ {0x8029, 97},
+ {0xc038, 97},
+ },
+ /* 6 */
+ {
+ {0x8002, 99},
+ {0x8009, 99},
+ {0x8017, 99},
+ {0xc028, 99},
+ {0x8002, 101},
+ {0x8009, 101},
+ {0x8017, 101},
+ {0xc028, 101},
+ {0x8002, 105},
+ {0x8009, 105},
+ {0x8017, 105},
+ {0xc028, 105},
+ {0x8002, 111},
+ {0x8009, 111},
+ {0x8017, 111},
+ {0xc028, 111},
+ },
+ /* 7 */
+ {
+ {0x8003, 99},
+ {0x8006, 99},
+ {0x800a, 99},
+ {0x800f, 99},
+ {0x8018, 99},
+ {0x801f, 99},
+ {0x8029, 99},
+ {0xc038, 99},
+ {0x8003, 101},
+ {0x8006, 101},
+ {0x800a, 101},
+ {0x800f, 101},
+ {0x8018, 101},
+ {0x801f, 101},
+ {0x8029, 101},
+ {0xc038, 101},
+ },
+ /* 8 */
+ {
+ {0x8003, 105},
+ {0x8006, 105},
+ {0x800a, 105},
+ {0x800f, 105},
+ {0x8018, 105},
+ {0x801f, 105},
+ {0x8029, 105},
+ {0xc038, 105},
+ {0x8003, 111},
+ {0x8006, 111},
+ {0x800a, 111},
+ {0x800f, 111},
+ {0x8018, 111},
+ {0x801f, 111},
+ {0x8029, 111},
+ {0xc038, 111},
+ },
+ /* 9 */
+ {
+ {0x8001, 115},
+ {0xc016, 115},
+ {0x8001, 116},
+ {0xc016, 116},
+ {0xc000, 32},
+ {0xc000, 37},
+ {0xc000, 45},
+ {0xc000, 46},
+ {0xc000, 47},
+ {0xc000, 51},
+ {0xc000, 52},
+ {0xc000, 53},
+ {0xc000, 54},
+ {0xc000, 55},
+ {0xc000, 56},
+ {0xc000, 57},
+ },
+ /* 10 */
+ {
+ {0x8002, 115},
+ {0x8009, 115},
+ {0x8017, 115},
+ {0xc028, 115},
+ {0x8002, 116},
+ {0x8009, 116},
+ {0x8017, 116},
+ {0xc028, 116},
+ {0x8001, 32},
+ {0xc016, 32},
+ {0x8001, 37},
+ {0xc016, 37},
+ {0x8001, 45},
+ {0xc016, 45},
+ {0x8001, 46},
+ {0xc016, 46},
+ },
+ /* 11 */
+ {
+ {0x8003, 115},
+ {0x8006, 115},
+ {0x800a, 115},
+ {0x800f, 115},
+ {0x8018, 115},
+ {0x801f, 115},
+ {0x8029, 115},
+ {0xc038, 115},
+ {0x8003, 116},
+ {0x8006, 116},
+ {0x800a, 116},
+ {0x800f, 116},
+ {0x8018, 116},
+ {0x801f, 116},
+ {0x8029, 116},
+ {0xc038, 116},
+ },
+ /* 12 */
+ {
+ {0x8002, 32},
+ {0x8009, 32},
+ {0x8017, 32},
+ {0xc028, 32},
+ {0x8002, 37},
+ {0x8009, 37},
+ {0x8017, 37},
+ {0xc028, 37},
+ {0x8002, 45},
+ {0x8009, 45},
+ {0x8017, 45},
+ {0xc028, 45},
+ {0x8002, 46},
+ {0x8009, 46},
+ {0x8017, 46},
+ {0xc028, 46},
+ },
+ /* 13 */
+ {
+ {0x8003, 32},
+ {0x8006, 32},
+ {0x800a, 32},
+ {0x800f, 32},
+ {0x8018, 32},
+ {0x801f, 32},
+ {0x8029, 32},
+ {0xc038, 32},
+ {0x8003, 37},
+ {0x8006, 37},
+ {0x800a, 37},
+ {0x800f, 37},
+ {0x8018, 37},
+ {0x801f, 37},
+ {0x8029, 37},
+ {0xc038, 37},
+ },
+ /* 14 */
+ {
+ {0x8003, 45},
+ {0x8006, 45},
+ {0x800a, 45},
+ {0x800f, 45},
+ {0x8018, 45},
+ {0x801f, 45},
+ {0x8029, 45},
+ {0xc038, 45},
+ {0x8003, 46},
+ {0x8006, 46},
+ {0x800a, 46},
+ {0x800f, 46},
+ {0x8018, 46},
+ {0x801f, 46},
+ {0x8029, 46},
+ {0xc038, 46},
+ },
+ /* 15 */
+ {
+ {0x8001, 47},
+ {0xc016, 47},
+ {0x8001, 51},
+ {0xc016, 51},
+ {0x8001, 52},
+ {0xc016, 52},
+ {0x8001, 53},
+ {0xc016, 53},
+ {0x8001, 54},
+ {0xc016, 54},
+ {0x8001, 55},
+ {0xc016, 55},
+ {0x8001, 56},
+ {0xc016, 56},
+ {0x8001, 57},
+ {0xc016, 57},
+ },
+ /* 16 */
+ {
+ {0x8002, 47},
+ {0x8009, 47},
+ {0x8017, 47},
+ {0xc028, 47},
+ {0x8002, 51},
+ {0x8009, 51},
+ {0x8017, 51},
+ {0xc028, 51},
+ {0x8002, 52},
+ {0x8009, 52},
+ {0x8017, 52},
+ {0xc028, 52},
+ {0x8002, 53},
+ {0x8009, 53},
+ {0x8017, 53},
+ {0xc028, 53},
+ },
+ /* 17 */
+ {
+ {0x8003, 47},
+ {0x8006, 47},
+ {0x800a, 47},
+ {0x800f, 47},
+ {0x8018, 47},
+ {0x801f, 47},
+ {0x8029, 47},
+ {0xc038, 47},
+ {0x8003, 51},
+ {0x8006, 51},
+ {0x800a, 51},
+ {0x800f, 51},
+ {0x8018, 51},
+ {0x801f, 51},
+ {0x8029, 51},
+ {0xc038, 51},
+ },
+ /* 18 */
+ {
+ {0x8003, 52},
+ {0x8006, 52},
+ {0x800a, 52},
+ {0x800f, 52},
+ {0x8018, 52},
+ {0x801f, 52},
+ {0x8029, 52},
+ {0xc038, 52},
+ {0x8003, 53},
+ {0x8006, 53},
+ {0x800a, 53},
+ {0x800f, 53},
+ {0x8018, 53},
+ {0x801f, 53},
+ {0x8029, 53},
+ {0xc038, 53},
+ },
+ /* 19 */
+ {
+ {0x8002, 54},
+ {0x8009, 54},
+ {0x8017, 54},
+ {0xc028, 54},
+ {0x8002, 55},
+ {0x8009, 55},
+ {0x8017, 55},
+ {0xc028, 55},
+ {0x8002, 56},
+ {0x8009, 56},
+ {0x8017, 56},
+ {0xc028, 56},
+ {0x8002, 57},
+ {0x8009, 57},
+ {0x8017, 57},
+ {0xc028, 57},
+ },
+ /* 20 */
+ {
+ {0x8003, 54},
+ {0x8006, 54},
+ {0x800a, 54},
+ {0x800f, 54},
+ {0x8018, 54},
+ {0x801f, 54},
+ {0x8029, 54},
+ {0xc038, 54},
+ {0x8003, 55},
+ {0x8006, 55},
+ {0x800a, 55},
+ {0x800f, 55},
+ {0x8018, 55},
+ {0x801f, 55},
+ {0x8029, 55},
+ {0xc038, 55},
+ },
+ /* 21 */
+ {
+ {0x8003, 56},
+ {0x8006, 56},
+ {0x800a, 56},
+ {0x800f, 56},
+ {0x8018, 56},
+ {0x801f, 56},
+ {0x8029, 56},
+ {0xc038, 56},
+ {0x8003, 57},
+ {0x8006, 57},
+ {0x800a, 57},
+ {0x800f, 57},
+ {0x8018, 57},
+ {0x801f, 57},
+ {0x8029, 57},
+ {0xc038, 57},
+ },
+ /* 22 */
+ {
+ {0x1a, 0},
+ {0x1b, 0},
+ {0x1d, 0},
+ {0x1e, 0},
+ {0x21, 0},
+ {0x22, 0},
+ {0x24, 0},
+ {0x25, 0},
+ {0x2b, 0},
+ {0x2e, 0},
+ {0x32, 0},
+ {0x35, 0},
+ {0x3a, 0},
+ {0x3d, 0},
+ {0x41, 0},
+ {0x4044, 0},
+ },
+ /* 23 */
+ {
+ {0xc000, 61},
+ {0xc000, 65},
+ {0xc000, 95},
+ {0xc000, 98},
+ {0xc000, 100},
+ {0xc000, 102},
+ {0xc000, 103},
+ {0xc000, 104},
+ {0xc000, 108},
+ {0xc000, 109},
+ {0xc000, 110},
+ {0xc000, 112},
+ {0xc000, 114},
+ {0xc000, 117},
+ {0x26, 0},
+ {0x27, 0},
+ },
+ /* 24 */
+ {
+ {0x8001, 61},
+ {0xc016, 61},
+ {0x8001, 65},
+ {0xc016, 65},
+ {0x8001, 95},
+ {0xc016, 95},
+ {0x8001, 98},
+ {0xc016, 98},
+ {0x8001, 100},
+ {0xc016, 100},
+ {0x8001, 102},
+ {0xc016, 102},
+ {0x8001, 103},
+ {0xc016, 103},
+ {0x8001, 104},
+ {0xc016, 104},
+ },
+ /* 25 */
+ {
+ {0x8002, 61},
+ {0x8009, 61},
+ {0x8017, 61},
+ {0xc028, 61},
+ {0x8002, 65},
+ {0x8009, 65},
+ {0x8017, 65},
+ {0xc028, 65},
+ {0x8002, 95},
+ {0x8009, 95},
+ {0x8017, 95},
+ {0xc028, 95},
+ {0x8002, 98},
+ {0x8009, 98},
+ {0x8017, 98},
+ {0xc028, 98},
+ },
+ /* 26 */
+ {
+ {0x8003, 61},
+ {0x8006, 61},
+ {0x800a, 61},
+ {0x800f, 61},
+ {0x8018, 61},
+ {0x801f, 61},
+ {0x8029, 61},
+ {0xc038, 61},
+ {0x8003, 65},
+ {0x8006, 65},
+ {0x800a, 65},
+ {0x800f, 65},
+ {0x8018, 65},
+ {0x801f, 65},
+ {0x8029, 65},
+ {0xc038, 65},
+ },
+ /* 27 */
+ {
+ {0x8003, 95},
+ {0x8006, 95},
+ {0x800a, 95},
+ {0x800f, 95},
+ {0x8018, 95},
+ {0x801f, 95},
+ {0x8029, 95},
+ {0xc038, 95},
+ {0x8003, 98},
+ {0x8006, 98},
+ {0x800a, 98},
+ {0x800f, 98},
+ {0x8018, 98},
+ {0x801f, 98},
+ {0x8029, 98},
+ {0xc038, 98},
+ },
+ /* 28 */
+ {
+ {0x8002, 100},
+ {0x8009, 100},
+ {0x8017, 100},
+ {0xc028, 100},
+ {0x8002, 102},
+ {0x8009, 102},
+ {0x8017, 102},
+ {0xc028, 102},
+ {0x8002, 103},
+ {0x8009, 103},
+ {0x8017, 103},
+ {0xc028, 103},
+ {0x8002, 104},
+ {0x8009, 104},
+ {0x8017, 104},
+ {0xc028, 104},
+ },
+ /* 29 */
+ {
+ {0x8003, 100},
+ {0x8006, 100},
+ {0x800a, 100},
+ {0x800f, 100},
+ {0x8018, 100},
+ {0x801f, 100},
+ {0x8029, 100},
+ {0xc038, 100},
+ {0x8003, 102},
+ {0x8006, 102},
+ {0x800a, 102},
+ {0x800f, 102},
+ {0x8018, 102},
+ {0x801f, 102},
+ {0x8029, 102},
+ {0xc038, 102},
+ },
+ /* 30 */
+ {
+ {0x8003, 103},
+ {0x8006, 103},
+ {0x800a, 103},
+ {0x800f, 103},
+ {0x8018, 103},
+ {0x801f, 103},
+ {0x8029, 103},
+ {0xc038, 103},
+ {0x8003, 104},
+ {0x8006, 104},
+ {0x800a, 104},
+ {0x800f, 104},
+ {0x8018, 104},
+ {0x801f, 104},
+ {0x8029, 104},
+ {0xc038, 104},
+ },
+ /* 31 */
+ {
+ {0x8001, 108},
+ {0xc016, 108},
+ {0x8001, 109},
+ {0xc016, 109},
+ {0x8001, 110},
+ {0xc016, 110},
+ {0x8001, 112},
+ {0xc016, 112},
+ {0x8001, 114},
+ {0xc016, 114},
+ {0x8001, 117},
+ {0xc016, 117},
+ {0xc000, 58},
+ {0xc000, 66},
+ {0xc000, 67},
+ {0xc000, 68},
+ },
+ /* 32 */
+ {
+ {0x8002, 108},
+ {0x8009, 108},
+ {0x8017, 108},
+ {0xc028, 108},
+ {0x8002, 109},
+ {0x8009, 109},
+ {0x8017, 109},
+ {0xc028, 109},
+ {0x8002, 110},
+ {0x8009, 110},
+ {0x8017, 110},
+ {0xc028, 110},
+ {0x8002, 112},
+ {0x8009, 112},
+ {0x8017, 112},
+ {0xc028, 112},
+ },
+ /* 33 */
+ {
+ {0x8003, 108},
+ {0x8006, 108},
+ {0x800a, 108},
+ {0x800f, 108},
+ {0x8018, 108},
+ {0x801f, 108},
+ {0x8029, 108},
+ {0xc038, 108},
+ {0x8003, 109},
+ {0x8006, 109},
+ {0x800a, 109},
+ {0x800f, 109},
+ {0x8018, 109},
+ {0x801f, 109},
+ {0x8029, 109},
+ {0xc038, 109},
+ },
+ /* 34 */
+ {
+ {0x8003, 110},
+ {0x8006, 110},
+ {0x800a, 110},
+ {0x800f, 110},
+ {0x8018, 110},
+ {0x801f, 110},
+ {0x8029, 110},
+ {0xc038, 110},
+ {0x8003, 112},
+ {0x8006, 112},
+ {0x800a, 112},
+ {0x800f, 112},
+ {0x8018, 112},
+ {0x801f, 112},
+ {0x8029, 112},
+ {0xc038, 112},
+ },
+ /* 35 */
+ {
+ {0x8002, 114},
+ {0x8009, 114},
+ {0x8017, 114},
+ {0xc028, 114},
+ {0x8002, 117},
+ {0x8009, 117},
+ {0x8017, 117},
+ {0xc028, 117},
+ {0x8001, 58},
+ {0xc016, 58},
+ {0x8001, 66},
+ {0xc016, 66},
+ {0x8001, 67},
+ {0xc016, 67},
+ {0x8001, 68},
+ {0xc016, 68},
+ },
+ /* 36 */
+ {
+ {0x8003, 114},
+ {0x8006, 114},
+ {0x800a, 114},
+ {0x800f, 114},
+ {0x8018, 114},
+ {0x801f, 114},
+ {0x8029, 114},
+ {0xc038, 114},
+ {0x8003, 117},
+ {0x8006, 117},
+ {0x800a, 117},
+ {0x800f, 117},
+ {0x8018, 117},
+ {0x801f, 117},
+ {0x8029, 117},
+ {0xc038, 117},
+ },
+ /* 37 */
+ {
+ {0x8002, 58},
+ {0x8009, 58},
+ {0x8017, 58},
+ {0xc028, 58},
+ {0x8002, 66},
+ {0x8009, 66},
+ {0x8017, 66},
+ {0xc028, 66},
+ {0x8002, 67},
+ {0x8009, 67},
+ {0x8017, 67},
+ {0xc028, 67},
+ {0x8002, 68},
+ {0x8009, 68},
+ {0x8017, 68},
+ {0xc028, 68},
+ },
+ /* 38 */
+ {
+ {0x8003, 58},
+ {0x8006, 58},
+ {0x800a, 58},
+ {0x800f, 58},
+ {0x8018, 58},
+ {0x801f, 58},
+ {0x8029, 58},
+ {0xc038, 58},
+ {0x8003, 66},
+ {0x8006, 66},
+ {0x800a, 66},
+ {0x800f, 66},
+ {0x8018, 66},
+ {0x801f, 66},
+ {0x8029, 66},
+ {0xc038, 66},
+ },
+ /* 39 */
+ {
+ {0x8003, 67},
+ {0x8006, 67},
+ {0x800a, 67},
+ {0x800f, 67},
+ {0x8018, 67},
+ {0x801f, 67},
+ {0x8029, 67},
+ {0xc038, 67},
+ {0x8003, 68},
+ {0x8006, 68},
+ {0x800a, 68},
+ {0x800f, 68},
+ {0x8018, 68},
+ {0x801f, 68},
+ {0x8029, 68},
+ {0xc038, 68},
+ },
+ /* 40 */
+ {
+ {0x2c, 0},
+ {0x2d, 0},
+ {0x2f, 0},
+ {0x30, 0},
+ {0x33, 0},
+ {0x34, 0},
+ {0x36, 0},
+ {0x37, 0},
+ {0x3b, 0},
+ {0x3c, 0},
+ {0x3e, 0},
+ {0x3f, 0},
+ {0x42, 0},
+ {0x43, 0},
+ {0x45, 0},
+ {0x4048, 0},
+ },
+ /* 41 */
+ {
+ {0xc000, 69},
+ {0xc000, 70},
+ {0xc000, 71},
+ {0xc000, 72},
+ {0xc000, 73},
+ {0xc000, 74},
+ {0xc000, 75},
+ {0xc000, 76},
+ {0xc000, 77},
+ {0xc000, 78},
+ {0xc000, 79},
+ {0xc000, 80},
+ {0xc000, 81},
+ {0xc000, 82},
+ {0xc000, 83},
+ {0xc000, 84},
+ },
+ /* 42 */
+ {
+ {0x8001, 69},
+ {0xc016, 69},
+ {0x8001, 70},
+ {0xc016, 70},
+ {0x8001, 71},
+ {0xc016, 71},
+ {0x8001, 72},
+ {0xc016, 72},
+ {0x8001, 73},
+ {0xc016, 73},
+ {0x8001, 74},
+ {0xc016, 74},
+ {0x8001, 75},
+ {0xc016, 75},
+ {0x8001, 76},
+ {0xc016, 76},
+ },
+ /* 43 */
+ {
+ {0x8002, 69},
+ {0x8009, 69},
+ {0x8017, 69},
+ {0xc028, 69},
+ {0x8002, 70},
+ {0x8009, 70},
+ {0x8017, 70},
+ {0xc028, 70},
+ {0x8002, 71},
+ {0x8009, 71},
+ {0x8017, 71},
+ {0xc028, 71},
+ {0x8002, 72},
+ {0x8009, 72},
+ {0x8017, 72},
+ {0xc028, 72},
+ },
+ /* 44 */
+ {
+ {0x8003, 69},
+ {0x8006, 69},
+ {0x800a, 69},
+ {0x800f, 69},
+ {0x8018, 69},
+ {0x801f, 69},
+ {0x8029, 69},
+ {0xc038, 69},
+ {0x8003, 70},
+ {0x8006, 70},
+ {0x800a, 70},
+ {0x800f, 70},
+ {0x8018, 70},
+ {0x801f, 70},
+ {0x8029, 70},
+ {0xc038, 70},
+ },
+ /* 45 */
+ {
+ {0x8003, 71},
+ {0x8006, 71},
+ {0x800a, 71},
+ {0x800f, 71},
+ {0x8018, 71},
+ {0x801f, 71},
+ {0x8029, 71},
+ {0xc038, 71},
+ {0x8003, 72},
+ {0x8006, 72},
+ {0x800a, 72},
+ {0x800f, 72},
+ {0x8018, 72},
+ {0x801f, 72},
+ {0x8029, 72},
+ {0xc038, 72},
+ },
+ /* 46 */
+ {
+ {0x8002, 73},
+ {0x8009, 73},
+ {0x8017, 73},
+ {0xc028, 73},
+ {0x8002, 74},
+ {0x8009, 74},
+ {0x8017, 74},
+ {0xc028, 74},
+ {0x8002, 75},
+ {0x8009, 75},
+ {0x8017, 75},
+ {0xc028, 75},
+ {0x8002, 76},
+ {0x8009, 76},
+ {0x8017, 76},
+ {0xc028, 76},
+ },
+ /* 47 */
+ {
+ {0x8003, 73},
+ {0x8006, 73},
+ {0x800a, 73},
+ {0x800f, 73},
+ {0x8018, 73},
+ {0x801f, 73},
+ {0x8029, 73},
+ {0xc038, 73},
+ {0x8003, 74},
+ {0x8006, 74},
+ {0x800a, 74},
+ {0x800f, 74},
+ {0x8018, 74},
+ {0x801f, 74},
+ {0x8029, 74},
+ {0xc038, 74},
+ },
+ /* 48 */
+ {
+ {0x8003, 75},
+ {0x8006, 75},
+ {0x800a, 75},
+ {0x800f, 75},
+ {0x8018, 75},
+ {0x801f, 75},
+ {0x8029, 75},
+ {0xc038, 75},
+ {0x8003, 76},
+ {0x8006, 76},
+ {0x800a, 76},
+ {0x800f, 76},
+ {0x8018, 76},
+ {0x801f, 76},
+ {0x8029, 76},
+ {0xc038, 76},
+ },
+ /* 49 */
+ {
+ {0x8001, 77},
+ {0xc016, 77},
+ {0x8001, 78},
+ {0xc016, 78},
+ {0x8001, 79},
+ {0xc016, 79},
+ {0x8001, 80},
+ {0xc016, 80},
+ {0x8001, 81},
+ {0xc016, 81},
+ {0x8001, 82},
+ {0xc016, 82},
+ {0x8001, 83},
+ {0xc016, 83},
+ {0x8001, 84},
+ {0xc016, 84},
+ },
+ /* 50 */
+ {
+ {0x8002, 77},
+ {0x8009, 77},
+ {0x8017, 77},
+ {0xc028, 77},
+ {0x8002, 78},
+ {0x8009, 78},
+ {0x8017, 78},
+ {0xc028, 78},
+ {0x8002, 79},
+ {0x8009, 79},
+ {0x8017, 79},
+ {0xc028, 79},
+ {0x8002, 80},
+ {0x8009, 80},
+ {0x8017, 80},
+ {0xc028, 80},
+ },
+ /* 51 */
+ {
+ {0x8003, 77},
+ {0x8006, 77},
+ {0x800a, 77},
+ {0x800f, 77},
+ {0x8018, 77},
+ {0x801f, 77},
+ {0x8029, 77},
+ {0xc038, 77},
+ {0x8003, 78},
+ {0x8006, 78},
+ {0x800a, 78},
+ {0x800f, 78},
+ {0x8018, 78},
+ {0x801f, 78},
+ {0x8029, 78},
+ {0xc038, 78},
+ },
+ /* 52 */
+ {
+ {0x8003, 79},
+ {0x8006, 79},
+ {0x800a, 79},
+ {0x800f, 79},
+ {0x8018, 79},
+ {0x801f, 79},
+ {0x8029, 79},
+ {0xc038, 79},
+ {0x8003, 80},
+ {0x8006, 80},
+ {0x800a, 80},
+ {0x800f, 80},
+ {0x8018, 80},
+ {0x801f, 80},
+ {0x8029, 80},
+ {0xc038, 80},
+ },
+ /* 53 */
+ {
+ {0x8002, 81},
+ {0x8009, 81},
+ {0x8017, 81},
+ {0xc028, 81},
+ {0x8002, 82},
+ {0x8009, 82},
+ {0x8017, 82},
+ {0xc028, 82},
+ {0x8002, 83},
+ {0x8009, 83},
+ {0x8017, 83},
+ {0xc028, 83},
+ {0x8002, 84},
+ {0x8009, 84},
+ {0x8017, 84},
+ {0xc028, 84},
+ },
+ /* 54 */
+ {
+ {0x8003, 81},
+ {0x8006, 81},
+ {0x800a, 81},
+ {0x800f, 81},
+ {0x8018, 81},
+ {0x801f, 81},
+ {0x8029, 81},
+ {0xc038, 81},
+ {0x8003, 82},
+ {0x8006, 82},
+ {0x800a, 82},
+ {0x800f, 82},
+ {0x8018, 82},
+ {0x801f, 82},
+ {0x8029, 82},
+ {0xc038, 82},
+ },
+ /* 55 */
+ {
+ {0x8003, 83},
+ {0x8006, 83},
+ {0x800a, 83},
+ {0x800f, 83},
+ {0x8018, 83},
+ {0x801f, 83},
+ {0x8029, 83},
+ {0xc038, 83},
+ {0x8003, 84},
+ {0x8006, 84},
+ {0x800a, 84},
+ {0x800f, 84},
+ {0x8018, 84},
+ {0x801f, 84},
+ {0x8029, 84},
+ {0xc038, 84},
+ },
+ /* 56 */
+ {
+ {0xc000, 85},
+ {0xc000, 86},
+ {0xc000, 87},
+ {0xc000, 89},
+ {0xc000, 106},
+ {0xc000, 107},
+ {0xc000, 113},
+ {0xc000, 118},
+ {0xc000, 119},
+ {0xc000, 120},
+ {0xc000, 121},
+ {0xc000, 122},
+ {0x46, 0},
+ {0x47, 0},
+ {0x49, 0},
+ {0x404a, 0},
+ },
+ /* 57 */
+ {
+ {0x8001, 85},
+ {0xc016, 85},
+ {0x8001, 86},
+ {0xc016, 86},
+ {0x8001, 87},
+ {0xc016, 87},
+ {0x8001, 89},
+ {0xc016, 89},
+ {0x8001, 106},
+ {0xc016, 106},
+ {0x8001, 107},
+ {0xc016, 107},
+ {0x8001, 113},
+ {0xc016, 113},
+ {0x8001, 118},
+ {0xc016, 118},
+ },
+ /* 58 */
+ {
+ {0x8002, 85},
+ {0x8009, 85},
+ {0x8017, 85},
+ {0xc028, 85},
+ {0x8002, 86},
+ {0x8009, 86},
+ {0x8017, 86},
+ {0xc028, 86},
+ {0x8002, 87},
+ {0x8009, 87},
+ {0x8017, 87},
+ {0xc028, 87},
+ {0x8002, 89},
+ {0x8009, 89},
+ {0x8017, 89},
+ {0xc028, 89},
+ },
+ /* 59 */
+ {
+ {0x8003, 85},
+ {0x8006, 85},
+ {0x800a, 85},
+ {0x800f, 85},
+ {0x8018, 85},
+ {0x801f, 85},
+ {0x8029, 85},
+ {0xc038, 85},
+ {0x8003, 86},
+ {0x8006, 86},
+ {0x800a, 86},
+ {0x800f, 86},
+ {0x8018, 86},
+ {0x801f, 86},
+ {0x8029, 86},
+ {0xc038, 86},
+ },
+ /* 60 */
+ {
+ {0x8003, 87},
+ {0x8006, 87},
+ {0x800a, 87},
+ {0x800f, 87},
+ {0x8018, 87},
+ {0x801f, 87},
+ {0x8029, 87},
+ {0xc038, 87},
+ {0x8003, 89},
+ {0x8006, 89},
+ {0x800a, 89},
+ {0x800f, 89},
+ {0x8018, 89},
+ {0x801f, 89},
+ {0x8029, 89},
+ {0xc038, 89},
+ },
+ /* 61 */
+ {
+ {0x8002, 106},
+ {0x8009, 106},
+ {0x8017, 106},
+ {0xc028, 106},
+ {0x8002, 107},
+ {0x8009, 107},
+ {0x8017, 107},
+ {0xc028, 107},
+ {0x8002, 113},
+ {0x8009, 113},
+ {0x8017, 113},
+ {0xc028, 113},
+ {0x8002, 118},
+ {0x8009, 118},
+ {0x8017, 118},
+ {0xc028, 118},
+ },
+ /* 62 */
+ {
+ {0x8003, 106},
+ {0x8006, 106},
+ {0x800a, 106},
+ {0x800f, 106},
+ {0x8018, 106},
+ {0x801f, 106},
+ {0x8029, 106},
+ {0xc038, 106},
+ {0x8003, 107},
+ {0x8006, 107},
+ {0x800a, 107},
+ {0x800f, 107},
+ {0x8018, 107},
+ {0x801f, 107},
+ {0x8029, 107},
+ {0xc038, 107},
+ },
+ /* 63 */
+ {
+ {0x8003, 113},
+ {0x8006, 113},
+ {0x800a, 113},
+ {0x800f, 113},
+ {0x8018, 113},
+ {0x801f, 113},
+ {0x8029, 113},
+ {0xc038, 113},
+ {0x8003, 118},
+ {0x8006, 118},
+ {0x800a, 118},
+ {0x800f, 118},
+ {0x8018, 118},
+ {0x801f, 118},
+ {0x8029, 118},
+ {0xc038, 118},
+ },
+ /* 64 */
+ {
+ {0x8001, 119},
+ {0xc016, 119},
+ {0x8001, 120},
+ {0xc016, 120},
+ {0x8001, 121},
+ {0xc016, 121},
+ {0x8001, 122},
+ {0xc016, 122},
+ {0xc000, 38},
+ {0xc000, 42},
+ {0xc000, 44},
+ {0xc000, 59},
+ {0xc000, 88},
+ {0xc000, 90},
+ {0x4b, 0},
+ {0x4e, 0},
+ },
+ /* 65 */
+ {
+ {0x8002, 119},
+ {0x8009, 119},
+ {0x8017, 119},
+ {0xc028, 119},
+ {0x8002, 120},
+ {0x8009, 120},
+ {0x8017, 120},
+ {0xc028, 120},
+ {0x8002, 121},
+ {0x8009, 121},
+ {0x8017, 121},
+ {0xc028, 121},
+ {0x8002, 122},
+ {0x8009, 122},
+ {0x8017, 122},
+ {0xc028, 122},
+ },
+ /* 66 */
+ {
+ {0x8003, 119},
+ {0x8006, 119},
+ {0x800a, 119},
+ {0x800f, 119},
+ {0x8018, 119},
+ {0x801f, 119},
+ {0x8029, 119},
+ {0xc038, 119},
+ {0x8003, 120},
+ {0x8006, 120},
+ {0x800a, 120},
+ {0x800f, 120},
+ {0x8018, 120},
+ {0x801f, 120},
+ {0x8029, 120},
+ {0xc038, 120},
+ },
+ /* 67 */
+ {
+ {0x8003, 121},
+ {0x8006, 121},
+ {0x800a, 121},
+ {0x800f, 121},
+ {0x8018, 121},
+ {0x801f, 121},
+ {0x8029, 121},
+ {0xc038, 121},
+ {0x8003, 122},
+ {0x8006, 122},
+ {0x800a, 122},
+ {0x800f, 122},
+ {0x8018, 122},
+ {0x801f, 122},
+ {0x8029, 122},
+ {0xc038, 122},
+ },
+ /* 68 */
+ {
+ {0x8001, 38},
+ {0xc016, 38},
+ {0x8001, 42},
+ {0xc016, 42},
+ {0x8001, 44},
+ {0xc016, 44},
+ {0x8001, 59},
+ {0xc016, 59},
+ {0x8001, 88},
+ {0xc016, 88},
+ {0x8001, 90},
+ {0xc016, 90},
+ {0x4c, 0},
+ {0x4d, 0},
+ {0x4f, 0},
+ {0x51, 0},
+ },
+ /* 69 */
+ {
+ {0x8002, 38},
+ {0x8009, 38},
+ {0x8017, 38},
+ {0xc028, 38},
+ {0x8002, 42},
+ {0x8009, 42},
+ {0x8017, 42},
+ {0xc028, 42},
+ {0x8002, 44},
+ {0x8009, 44},
+ {0x8017, 44},
+ {0xc028, 44},
+ {0x8002, 59},
+ {0x8009, 59},
+ {0x8017, 59},
+ {0xc028, 59},
+ },
+ /* 70 */
+ {
+ {0x8003, 38},
+ {0x8006, 38},
+ {0x800a, 38},
+ {0x800f, 38},
+ {0x8018, 38},
+ {0x801f, 38},
+ {0x8029, 38},
+ {0xc038, 38},
+ {0x8003, 42},
+ {0x8006, 42},
+ {0x800a, 42},
+ {0x800f, 42},
+ {0x8018, 42},
+ {0x801f, 42},
+ {0x8029, 42},
+ {0xc038, 42},
+ },
+ /* 71 */
+ {
+ {0x8003, 44},
+ {0x8006, 44},
+ {0x800a, 44},
+ {0x800f, 44},
+ {0x8018, 44},
+ {0x801f, 44},
+ {0x8029, 44},
+ {0xc038, 44},
+ {0x8003, 59},
+ {0x8006, 59},
+ {0x800a, 59},
+ {0x800f, 59},
+ {0x8018, 59},
+ {0x801f, 59},
+ {0x8029, 59},
+ {0xc038, 59},
+ },
+ /* 72 */
+ {
+ {0x8002, 88},
+ {0x8009, 88},
+ {0x8017, 88},
+ {0xc028, 88},
+ {0x8002, 90},
+ {0x8009, 90},
+ {0x8017, 90},
+ {0xc028, 90},
+ {0xc000, 33},
+ {0xc000, 34},
+ {0xc000, 40},
+ {0xc000, 41},
+ {0xc000, 63},
+ {0x50, 0},
+ {0x52, 0},
+ {0x54, 0},
+ },
+ /* 73 */
+ {
+ {0x8003, 88},
+ {0x8006, 88},
+ {0x800a, 88},
+ {0x800f, 88},
+ {0x8018, 88},
+ {0x801f, 88},
+ {0x8029, 88},
+ {0xc038, 88},
+ {0x8003, 90},
+ {0x8006, 90},
+ {0x800a, 90},
+ {0x800f, 90},
+ {0x8018, 90},
+ {0x801f, 90},
+ {0x8029, 90},
+ {0xc038, 90},
+ },
+ /* 74 */
+ {
+ {0x8001, 33},
+ {0xc016, 33},
+ {0x8001, 34},
+ {0xc016, 34},
+ {0x8001, 40},
+ {0xc016, 40},
+ {0x8001, 41},
+ {0xc016, 41},
+ {0x8001, 63},
+ {0xc016, 63},
+ {0xc000, 39},
+ {0xc000, 43},
+ {0xc000, 124},
+ {0x53, 0},
+ {0x55, 0},
+ {0x58, 0},
+ },
+ /* 75 */
+ {
+ {0x8002, 33},
+ {0x8009, 33},
+ {0x8017, 33},
+ {0xc028, 33},
+ {0x8002, 34},
+ {0x8009, 34},
+ {0x8017, 34},
+ {0xc028, 34},
+ {0x8002, 40},
+ {0x8009, 40},
+ {0x8017, 40},
+ {0xc028, 40},
+ {0x8002, 41},
+ {0x8009, 41},
+ {0x8017, 41},
+ {0xc028, 41},
+ },
+ /* 76 */
+ {
+ {0x8003, 33},
+ {0x8006, 33},
+ {0x800a, 33},
+ {0x800f, 33},
+ {0x8018, 33},
+ {0x801f, 33},
+ {0x8029, 33},
+ {0xc038, 33},
+ {0x8003, 34},
+ {0x8006, 34},
+ {0x800a, 34},
+ {0x800f, 34},
+ {0x8018, 34},
+ {0x801f, 34},
+ {0x8029, 34},
+ {0xc038, 34},
+ },
+ /* 77 */
+ {
+ {0x8003, 40},
+ {0x8006, 40},
+ {0x800a, 40},
+ {0x800f, 40},
+ {0x8018, 40},
+ {0x801f, 40},
+ {0x8029, 40},
+ {0xc038, 40},
+ {0x8003, 41},
+ {0x8006, 41},
+ {0x800a, 41},
+ {0x800f, 41},
+ {0x8018, 41},
+ {0x801f, 41},
+ {0x8029, 41},
+ {0xc038, 41},
+ },
+ /* 78 */
+ {
+ {0x8002, 63},
+ {0x8009, 63},
+ {0x8017, 63},
+ {0xc028, 63},
+ {0x8001, 39},
+ {0xc016, 39},
+ {0x8001, 43},
+ {0xc016, 43},
+ {0x8001, 124},
+ {0xc016, 124},
+ {0xc000, 35},
+ {0xc000, 62},
+ {0x56, 0},
+ {0x57, 0},
+ {0x59, 0},
+ {0x5a, 0},
+ },
+ /* 79 */
+ {
+ {0x8003, 63},
+ {0x8006, 63},
+ {0x800a, 63},
+ {0x800f, 63},
+ {0x8018, 63},
+ {0x801f, 63},
+ {0x8029, 63},
+ {0xc038, 63},
+ {0x8002, 39},
+ {0x8009, 39},
+ {0x8017, 39},
+ {0xc028, 39},
+ {0x8002, 43},
+ {0x8009, 43},
+ {0x8017, 43},
+ {0xc028, 43},
+ },
+ /* 80 */
+ {
+ {0x8003, 39},
+ {0x8006, 39},
+ {0x800a, 39},
+ {0x800f, 39},
+ {0x8018, 39},
+ {0x801f, 39},
+ {0x8029, 39},
+ {0xc038, 39},
+ {0x8003, 43},
+ {0x8006, 43},
+ {0x800a, 43},
+ {0x800f, 43},
+ {0x8018, 43},
+ {0x801f, 43},
+ {0x8029, 43},
+ {0xc038, 43},
+ },
+ /* 81 */
+ {
+ {0x8002, 124},
+ {0x8009, 124},
+ {0x8017, 124},
+ {0xc028, 124},
+ {0x8001, 35},
+ {0xc016, 35},
+ {0x8001, 62},
+ {0xc016, 62},
+ {0xc000, 0},
+ {0xc000, 36},
+ {0xc000, 64},
+ {0xc000, 91},
+ {0xc000, 93},
+ {0xc000, 126},
+ {0x5b, 0},
+ {0x5c, 0},
+ },
+ /* 82 */
+ {
+ {0x8003, 124},
+ {0x8006, 124},
+ {0x800a, 124},
+ {0x800f, 124},
+ {0x8018, 124},
+ {0x801f, 124},
+ {0x8029, 124},
+ {0xc038, 124},
+ {0x8002, 35},
+ {0x8009, 35},
+ {0x8017, 35},
+ {0xc028, 35},
+ {0x8002, 62},
+ {0x8009, 62},
+ {0x8017, 62},
+ {0xc028, 62},
+ },
+ /* 83 */
+ {
+ {0x8003, 35},
+ {0x8006, 35},
+ {0x800a, 35},
+ {0x800f, 35},
+ {0x8018, 35},
+ {0x801f, 35},
+ {0x8029, 35},
+ {0xc038, 35},
+ {0x8003, 62},
+ {0x8006, 62},
+ {0x800a, 62},
+ {0x800f, 62},
+ {0x8018, 62},
+ {0x801f, 62},
+ {0x8029, 62},
+ {0xc038, 62},
+ },
+ /* 84 */
+ {
+ {0x8001, 0},
+ {0xc016, 0},
+ {0x8001, 36},
+ {0xc016, 36},
+ {0x8001, 64},
+ {0xc016, 64},
+ {0x8001, 91},
+ {0xc016, 91},
+ {0x8001, 93},
+ {0xc016, 93},
+ {0x8001, 126},
+ {0xc016, 126},
+ {0xc000, 94},
+ {0xc000, 125},
+ {0x5d, 0},
+ {0x5e, 0},
+ },
+ /* 85 */
+ {
+ {0x8002, 0},
+ {0x8009, 0},
+ {0x8017, 0},
+ {0xc028, 0},
+ {0x8002, 36},
+ {0x8009, 36},
+ {0x8017, 36},
+ {0xc028, 36},
+ {0x8002, 64},
+ {0x8009, 64},
+ {0x8017, 64},
+ {0xc028, 64},
+ {0x8002, 91},
+ {0x8009, 91},
+ {0x8017, 91},
+ {0xc028, 91},
+ },
+ /* 86 */
+ {
+ {0x8003, 0},
+ {0x8006, 0},
+ {0x800a, 0},
+ {0x800f, 0},
+ {0x8018, 0},
+ {0x801f, 0},
+ {0x8029, 0},
+ {0xc038, 0},
+ {0x8003, 36},
+ {0x8006, 36},
+ {0x800a, 36},
+ {0x800f, 36},
+ {0x8018, 36},
+ {0x801f, 36},
+ {0x8029, 36},
+ {0xc038, 36},
+ },
+ /* 87 */
+ {
+ {0x8003, 64},
+ {0x8006, 64},
+ {0x800a, 64},
+ {0x800f, 64},
+ {0x8018, 64},
+ {0x801f, 64},
+ {0x8029, 64},
+ {0xc038, 64},
+ {0x8003, 91},
+ {0x8006, 91},
+ {0x800a, 91},
+ {0x800f, 91},
+ {0x8018, 91},
+ {0x801f, 91},
+ {0x8029, 91},
+ {0xc038, 91},
+ },
+ /* 88 */
+ {
+ {0x8002, 93},
+ {0x8009, 93},
+ {0x8017, 93},
+ {0xc028, 93},
+ {0x8002, 126},
+ {0x8009, 126},
+ {0x8017, 126},
+ {0xc028, 126},
+ {0x8001, 94},
+ {0xc016, 94},
+ {0x8001, 125},
+ {0xc016, 125},
+ {0xc000, 60},
+ {0xc000, 96},
+ {0xc000, 123},
+ {0x5f, 0},
+ },
+ /* 89 */
+ {
+ {0x8003, 93},
+ {0x8006, 93},
+ {0x800a, 93},
+ {0x800f, 93},
+ {0x8018, 93},
+ {0x801f, 93},
+ {0x8029, 93},
+ {0xc038, 93},
+ {0x8003, 126},
+ {0x8006, 126},
+ {0x800a, 126},
+ {0x800f, 126},
+ {0x8018, 126},
+ {0x801f, 126},
+ {0x8029, 126},
+ {0xc038, 126},
+ },
+ /* 90 */
+ {
+ {0x8002, 94},
+ {0x8009, 94},
+ {0x8017, 94},
+ {0xc028, 94},
+ {0x8002, 125},
+ {0x8009, 125},
+ {0x8017, 125},
+ {0xc028, 125},
+ {0x8001, 60},
+ {0xc016, 60},
+ {0x8001, 96},
+ {0xc016, 96},
+ {0x8001, 123},
+ {0xc016, 123},
+ {0x60, 0},
+ {0x6e, 0},
+ },
+ /* 91 */
+ {
+ {0x8003, 94},
+ {0x8006, 94},
+ {0x800a, 94},
+ {0x800f, 94},
+ {0x8018, 94},
+ {0x801f, 94},
+ {0x8029, 94},
+ {0xc038, 94},
+ {0x8003, 125},
+ {0x8006, 125},
+ {0x800a, 125},
+ {0x800f, 125},
+ {0x8018, 125},
+ {0x801f, 125},
+ {0x8029, 125},
+ {0xc038, 125},
+ },
+ /* 92 */
+ {
+ {0x8002, 60},
+ {0x8009, 60},
+ {0x8017, 60},
+ {0xc028, 60},
+ {0x8002, 96},
+ {0x8009, 96},
+ {0x8017, 96},
+ {0xc028, 96},
+ {0x8002, 123},
+ {0x8009, 123},
+ {0x8017, 123},
+ {0xc028, 123},
+ {0x61, 0},
+ {0x65, 0},
+ {0x6f, 0},
+ {0x85, 0},
+ },
+ /* 93 */
+ {
+ {0x8003, 60},
+ {0x8006, 60},
+ {0x800a, 60},
+ {0x800f, 60},
+ {0x8018, 60},
+ {0x801f, 60},
+ {0x8029, 60},
+ {0xc038, 60},
+ {0x8003, 96},
+ {0x8006, 96},
+ {0x800a, 96},
+ {0x800f, 96},
+ {0x8018, 96},
+ {0x801f, 96},
+ {0x8029, 96},
+ {0xc038, 96},
+ },
+ /* 94 */
+ {
+ {0x8003, 123},
+ {0x8006, 123},
+ {0x800a, 123},
+ {0x800f, 123},
+ {0x8018, 123},
+ {0x801f, 123},
+ {0x8029, 123},
+ {0xc038, 123},
+ {0x62, 0},
+ {0x63, 0},
+ {0x66, 0},
+ {0x69, 0},
+ {0x70, 0},
+ {0x77, 0},
+ {0x86, 0},
+ {0x99, 0},
+ },
+ /* 95 */
+ {
+ {0xc000, 92},
+ {0xc000, 195},
+ {0xc000, 208},
+ {0x64, 0},
+ {0x67, 0},
+ {0x68, 0},
+ {0x6a, 0},
+ {0x6b, 0},
+ {0x71, 0},
+ {0x74, 0},
+ {0x78, 0},
+ {0x7e, 0},
+ {0x87, 0},
+ {0x8e, 0},
+ {0x9a, 0},
+ {0xa9, 0},
+ },
+ /* 96 */
+ {
+ {0x8001, 92},
+ {0xc016, 92},
+ {0x8001, 195},
+ {0xc016, 195},
+ {0x8001, 208},
+ {0xc016, 208},
+ {0xc000, 128},
+ {0xc000, 130},
+ {0xc000, 131},
+ {0xc000, 162},
+ {0xc000, 184},
+ {0xc000, 194},
+ {0xc000, 224},
+ {0xc000, 226},
+ {0x6c, 0},
+ {0x6d, 0},
+ },
+ /* 97 */
+ {
+ {0x8002, 92},
+ {0x8009, 92},
+ {0x8017, 92},
+ {0xc028, 92},
+ {0x8002, 195},
+ {0x8009, 195},
+ {0x8017, 195},
+ {0xc028, 195},
+ {0x8002, 208},
+ {0x8009, 208},
+ {0x8017, 208},
+ {0xc028, 208},
+ {0x8001, 128},
+ {0xc016, 128},
+ {0x8001, 130},
+ {0xc016, 130},
+ },
+ /* 98 */
+ {
+ {0x8003, 92},
+ {0x8006, 92},
+ {0x800a, 92},
+ {0x800f, 92},
+ {0x8018, 92},
+ {0x801f, 92},
+ {0x8029, 92},
+ {0xc038, 92},
+ {0x8003, 195},
+ {0x8006, 195},
+ {0x800a, 195},
+ {0x800f, 195},
+ {0x8018, 195},
+ {0x801f, 195},
+ {0x8029, 195},
+ {0xc038, 195},
+ },
+ /* 99 */
+ {
+ {0x8003, 208},
+ {0x8006, 208},
+ {0x800a, 208},
+ {0x800f, 208},
+ {0x8018, 208},
+ {0x801f, 208},
+ {0x8029, 208},
+ {0xc038, 208},
+ {0x8002, 128},
+ {0x8009, 128},
+ {0x8017, 128},
+ {0xc028, 128},
+ {0x8002, 130},
+ {0x8009, 130},
+ {0x8017, 130},
+ {0xc028, 130},
+ },
+ /* 100 */
+ {
+ {0x8003, 128},
+ {0x8006, 128},
+ {0x800a, 128},
+ {0x800f, 128},
+ {0x8018, 128},
+ {0x801f, 128},
+ {0x8029, 128},
+ {0xc038, 128},
+ {0x8003, 130},
+ {0x8006, 130},
+ {0x800a, 130},
+ {0x800f, 130},
+ {0x8018, 130},
+ {0x801f, 130},
+ {0x8029, 130},
+ {0xc038, 130},
+ },
+ /* 101 */
+ {
+ {0x8001, 131},
+ {0xc016, 131},
+ {0x8001, 162},
+ {0xc016, 162},
+ {0x8001, 184},
+ {0xc016, 184},
+ {0x8001, 194},
+ {0xc016, 194},
+ {0x8001, 224},
+ {0xc016, 224},
+ {0x8001, 226},
+ {0xc016, 226},
+ {0xc000, 153},
+ {0xc000, 161},
+ {0xc000, 167},
+ {0xc000, 172},
+ },
+ /* 102 */
+ {
+ {0x8002, 131},
+ {0x8009, 131},
+ {0x8017, 131},
+ {0xc028, 131},
+ {0x8002, 162},
+ {0x8009, 162},
+ {0x8017, 162},
+ {0xc028, 162},
+ {0x8002, 184},
+ {0x8009, 184},
+ {0x8017, 184},
+ {0xc028, 184},
+ {0x8002, 194},
+ {0x8009, 194},
+ {0x8017, 194},
+ {0xc028, 194},
+ },
+ /* 103 */
+ {
+ {0x8003, 131},
+ {0x8006, 131},
+ {0x800a, 131},
+ {0x800f, 131},
+ {0x8018, 131},
+ {0x801f, 131},
+ {0x8029, 131},
+ {0xc038, 131},
+ {0x8003, 162},
+ {0x8006, 162},
+ {0x800a, 162},
+ {0x800f, 162},
+ {0x8018, 162},
+ {0x801f, 162},
+ {0x8029, 162},
+ {0xc038, 162},
+ },
+ /* 104 */
+ {
+ {0x8003, 184},
+ {0x8006, 184},
+ {0x800a, 184},
+ {0x800f, 184},
+ {0x8018, 184},
+ {0x801f, 184},
+ {0x8029, 184},
+ {0xc038, 184},
+ {0x8003, 194},
+ {0x8006, 194},
+ {0x800a, 194},
+ {0x800f, 194},
+ {0x8018, 194},
+ {0x801f, 194},
+ {0x8029, 194},
+ {0xc038, 194},
+ },
+ /* 105 */
+ {
+ {0x8002, 224},
+ {0x8009, 224},
+ {0x8017, 224},
+ {0xc028, 224},
+ {0x8002, 226},
+ {0x8009, 226},
+ {0x8017, 226},
+ {0xc028, 226},
+ {0x8001, 153},
+ {0xc016, 153},
+ {0x8001, 161},
+ {0xc016, 161},
+ {0x8001, 167},
+ {0xc016, 167},
+ {0x8001, 172},
+ {0xc016, 172},
+ },
+ /* 106 */
+ {
+ {0x8003, 224},
+ {0x8006, 224},
+ {0x800a, 224},
+ {0x800f, 224},
+ {0x8018, 224},
+ {0x801f, 224},
+ {0x8029, 224},
+ {0xc038, 224},
+ {0x8003, 226},
+ {0x8006, 226},
+ {0x800a, 226},
+ {0x800f, 226},
+ {0x8018, 226},
+ {0x801f, 226},
+ {0x8029, 226},
+ {0xc038, 226},
+ },
+ /* 107 */
+ {
+ {0x8002, 153},
+ {0x8009, 153},
+ {0x8017, 153},
+ {0xc028, 153},
+ {0x8002, 161},
+ {0x8009, 161},
+ {0x8017, 161},
+ {0xc028, 161},
+ {0x8002, 167},
+ {0x8009, 167},
+ {0x8017, 167},
+ {0xc028, 167},
+ {0x8002, 172},
+ {0x8009, 172},
+ {0x8017, 172},
+ {0xc028, 172},
+ },
+ /* 108 */
+ {
+ {0x8003, 153},
+ {0x8006, 153},
+ {0x800a, 153},
+ {0x800f, 153},
+ {0x8018, 153},
+ {0x801f, 153},
+ {0x8029, 153},
+ {0xc038, 153},
+ {0x8003, 161},
+ {0x8006, 161},
+ {0x800a, 161},
+ {0x800f, 161},
+ {0x8018, 161},
+ {0x801f, 161},
+ {0x8029, 161},
+ {0xc038, 161},
+ },
+ /* 109 */
+ {
+ {0x8003, 167},
+ {0x8006, 167},
+ {0x800a, 167},
+ {0x800f, 167},
+ {0x8018, 167},
+ {0x801f, 167},
+ {0x8029, 167},
+ {0xc038, 167},
+ {0x8003, 172},
+ {0x8006, 172},
+ {0x800a, 172},
+ {0x800f, 172},
+ {0x8018, 172},
+ {0x801f, 172},
+ {0x8029, 172},
+ {0xc038, 172},
+ },
+ /* 110 */
+ {
+ {0x72, 0},
+ {0x73, 0},
+ {0x75, 0},
+ {0x76, 0},
+ {0x79, 0},
+ {0x7b, 0},
+ {0x7f, 0},
+ {0x82, 0},
+ {0x88, 0},
+ {0x8b, 0},
+ {0x8f, 0},
+ {0x92, 0},
+ {0x9b, 0},
+ {0xa2, 0},
+ {0xaa, 0},
+ {0xb4, 0},
+ },
+ /* 111 */
+ {
+ {0xc000, 176},
+ {0xc000, 177},
+ {0xc000, 179},
+ {0xc000, 209},
+ {0xc000, 216},
+ {0xc000, 217},
+ {0xc000, 227},
+ {0xc000, 229},
+ {0xc000, 230},
+ {0x7a, 0},
+ {0x7c, 0},
+ {0x7d, 0},
+ {0x80, 0},
+ {0x81, 0},
+ {0x83, 0},
+ {0x84, 0},
+ },
+ /* 112 */
+ {
+ {0x8001, 176},
+ {0xc016, 176},
+ {0x8001, 177},
+ {0xc016, 177},
+ {0x8001, 179},
+ {0xc016, 179},
+ {0x8001, 209},
+ {0xc016, 209},
+ {0x8001, 216},
+ {0xc016, 216},
+ {0x8001, 217},
+ {0xc016, 217},
+ {0x8001, 227},
+ {0xc016, 227},
+ {0x8001, 229},
+ {0xc016, 229},
+ },
+ /* 113 */
+ {
+ {0x8002, 176},
+ {0x8009, 176},
+ {0x8017, 176},
+ {0xc028, 176},
+ {0x8002, 177},
+ {0x8009, 177},
+ {0x8017, 177},
+ {0xc028, 177},
+ {0x8002, 179},
+ {0x8009, 179},
+ {0x8017, 179},
+ {0xc028, 179},
+ {0x8002, 209},
+ {0x8009, 209},
+ {0x8017, 209},
+ {0xc028, 209},
+ },
+ /* 114 */
+ {
+ {0x8003, 176},
+ {0x8006, 176},
+ {0x800a, 176},
+ {0x800f, 176},
+ {0x8018, 176},
+ {0x801f, 176},
+ {0x8029, 176},
+ {0xc038, 176},
+ {0x8003, 177},
+ {0x8006, 177},
+ {0x800a, 177},
+ {0x800f, 177},
+ {0x8018, 177},
+ {0x801f, 177},
+ {0x8029, 177},
+ {0xc038, 177},
+ },
+ /* 115 */
+ {
+ {0x8003, 179},
+ {0x8006, 179},
+ {0x800a, 179},
+ {0x800f, 179},
+ {0x8018, 179},
+ {0x801f, 179},
+ {0x8029, 179},
+ {0xc038, 179},
+ {0x8003, 209},
+ {0x8006, 209},
+ {0x800a, 209},
+ {0x800f, 209},
+ {0x8018, 209},
+ {0x801f, 209},
+ {0x8029, 209},
+ {0xc038, 209},
+ },
+ /* 116 */
+ {
+ {0x8002, 216},
+ {0x8009, 216},
+ {0x8017, 216},
+ {0xc028, 216},
+ {0x8002, 217},
+ {0x8009, 217},
+ {0x8017, 217},
+ {0xc028, 217},
+ {0x8002, 227},
+ {0x8009, 227},
+ {0x8017, 227},
+ {0xc028, 227},
+ {0x8002, 229},
+ {0x8009, 229},
+ {0x8017, 229},
+ {0xc028, 229},
+ },
+ /* 117 */
+ {
+ {0x8003, 216},
+ {0x8006, 216},
+ {0x800a, 216},
+ {0x800f, 216},
+ {0x8018, 216},
+ {0x801f, 216},
+ {0x8029, 216},
+ {0xc038, 216},
+ {0x8003, 217},
+ {0x8006, 217},
+ {0x800a, 217},
+ {0x800f, 217},
+ {0x8018, 217},
+ {0x801f, 217},
+ {0x8029, 217},
+ {0xc038, 217},
+ },
+ /* 118 */
+ {
+ {0x8003, 227},
+ {0x8006, 227},
+ {0x800a, 227},
+ {0x800f, 227},
+ {0x8018, 227},
+ {0x801f, 227},
+ {0x8029, 227},
+ {0xc038, 227},
+ {0x8003, 229},
+ {0x8006, 229},
+ {0x800a, 229},
+ {0x800f, 229},
+ {0x8018, 229},
+ {0x801f, 229},
+ {0x8029, 229},
+ {0xc038, 229},
+ },
+ /* 119 */
+ {
+ {0x8001, 230},
+ {0xc016, 230},
+ {0xc000, 129},
+ {0xc000, 132},
+ {0xc000, 133},
+ {0xc000, 134},
+ {0xc000, 136},
+ {0xc000, 146},
+ {0xc000, 154},
+ {0xc000, 156},
+ {0xc000, 160},
+ {0xc000, 163},
+ {0xc000, 164},
+ {0xc000, 169},
+ {0xc000, 170},
+ {0xc000, 173},
+ },
+ /* 120 */
+ {
+ {0x8002, 230},
+ {0x8009, 230},
+ {0x8017, 230},
+ {0xc028, 230},
+ {0x8001, 129},
+ {0xc016, 129},
+ {0x8001, 132},
+ {0xc016, 132},
+ {0x8001, 133},
+ {0xc016, 133},
+ {0x8001, 134},
+ {0xc016, 134},
+ {0x8001, 136},
+ {0xc016, 136},
+ {0x8001, 146},
+ {0xc016, 146},
+ },
+ /* 121 */
+ {
+ {0x8003, 230},
+ {0x8006, 230},
+ {0x800a, 230},
+ {0x800f, 230},
+ {0x8018, 230},
+ {0x801f, 230},
+ {0x8029, 230},
+ {0xc038, 230},
+ {0x8002, 129},
+ {0x8009, 129},
+ {0x8017, 129},
+ {0xc028, 129},
+ {0x8002, 132},
+ {0x8009, 132},
+ {0x8017, 132},
+ {0xc028, 132},
+ },
+ /* 122 */
+ {
+ {0x8003, 129},
+ {0x8006, 129},
+ {0x800a, 129},
+ {0x800f, 129},
+ {0x8018, 129},
+ {0x801f, 129},
+ {0x8029, 129},
+ {0xc038, 129},
+ {0x8003, 132},
+ {0x8006, 132},
+ {0x800a, 132},
+ {0x800f, 132},
+ {0x8018, 132},
+ {0x801f, 132},
+ {0x8029, 132},
+ {0xc038, 132},
+ },
+ /* 123 */
+ {
+ {0x8002, 133},
+ {0x8009, 133},
+ {0x8017, 133},
+ {0xc028, 133},
+ {0x8002, 134},
+ {0x8009, 134},
+ {0x8017, 134},
+ {0xc028, 134},
+ {0x8002, 136},
+ {0x8009, 136},
+ {0x8017, 136},
+ {0xc028, 136},
+ {0x8002, 146},
+ {0x8009, 146},
+ {0x8017, 146},
+ {0xc028, 146},
+ },
+ /* 124 */
+ {
+ {0x8003, 133},
+ {0x8006, 133},
+ {0x800a, 133},
+ {0x800f, 133},
+ {0x8018, 133},
+ {0x801f, 133},
+ {0x8029, 133},
+ {0xc038, 133},
+ {0x8003, 134},
+ {0x8006, 134},
+ {0x800a, 134},
+ {0x800f, 134},
+ {0x8018, 134},
+ {0x801f, 134},
+ {0x8029, 134},
+ {0xc038, 134},
+ },
+ /* 125 */
+ {
+ {0x8003, 136},
+ {0x8006, 136},
+ {0x800a, 136},
+ {0x800f, 136},
+ {0x8018, 136},
+ {0x801f, 136},
+ {0x8029, 136},
+ {0xc038, 136},
+ {0x8003, 146},
+ {0x8006, 146},
+ {0x800a, 146},
+ {0x800f, 146},
+ {0x8018, 146},
+ {0x801f, 146},
+ {0x8029, 146},
+ {0xc038, 146},
+ },
+ /* 126 */
+ {
+ {0x8001, 154},
+ {0xc016, 154},
+ {0x8001, 156},
+ {0xc016, 156},
+ {0x8001, 160},
+ {0xc016, 160},
+ {0x8001, 163},
+ {0xc016, 163},
+ {0x8001, 164},
+ {0xc016, 164},
+ {0x8001, 169},
+ {0xc016, 169},
+ {0x8001, 170},
+ {0xc016, 170},
+ {0x8001, 173},
+ {0xc016, 173},
+ },
+ /* 127 */
+ {
+ {0x8002, 154},
+ {0x8009, 154},
+ {0x8017, 154},
+ {0xc028, 154},
+ {0x8002, 156},
+ {0x8009, 156},
+ {0x8017, 156},
+ {0xc028, 156},
+ {0x8002, 160},
+ {0x8009, 160},
+ {0x8017, 160},
+ {0xc028, 160},
+ {0x8002, 163},
+ {0x8009, 163},
+ {0x8017, 163},
+ {0xc028, 163},
+ },
+ /* 128 */
+ {
+ {0x8003, 154},
+ {0x8006, 154},
+ {0x800a, 154},
+ {0x800f, 154},
+ {0x8018, 154},
+ {0x801f, 154},
+ {0x8029, 154},
+ {0xc038, 154},
+ {0x8003, 156},
+ {0x8006, 156},
+ {0x800a, 156},
+ {0x800f, 156},
+ {0x8018, 156},
+ {0x801f, 156},
+ {0x8029, 156},
+ {0xc038, 156},
+ },
+ /* 129 */
+ {
+ {0x8003, 160},
+ {0x8006, 160},
+ {0x800a, 160},
+ {0x800f, 160},
+ {0x8018, 160},
+ {0x801f, 160},
+ {0x8029, 160},
+ {0xc038, 160},
+ {0x8003, 163},
+ {0x8006, 163},
+ {0x800a, 163},
+ {0x800f, 163},
+ {0x8018, 163},
+ {0x801f, 163},
+ {0x8029, 163},
+ {0xc038, 163},
+ },
+ /* 130 */
+ {
+ {0x8002, 164},
+ {0x8009, 164},
+ {0x8017, 164},
+ {0xc028, 164},
+ {0x8002, 169},
+ {0x8009, 169},
+ {0x8017, 169},
+ {0xc028, 169},
+ {0x8002, 170},
+ {0x8009, 170},
+ {0x8017, 170},
+ {0xc028, 170},
+ {0x8002, 173},
+ {0x8009, 173},
+ {0x8017, 173},
+ {0xc028, 173},
+ },
+ /* 131 */
+ {
+ {0x8003, 164},
+ {0x8006, 164},
+ {0x800a, 164},
+ {0x800f, 164},
+ {0x8018, 164},
+ {0x801f, 164},
+ {0x8029, 164},
+ {0xc038, 164},
+ {0x8003, 169},
+ {0x8006, 169},
+ {0x800a, 169},
+ {0x800f, 169},
+ {0x8018, 169},
+ {0x801f, 169},
+ {0x8029, 169},
+ {0xc038, 169},
+ },
+ /* 132 */
+ {
+ {0x8003, 170},
+ {0x8006, 170},
+ {0x800a, 170},
+ {0x800f, 170},
+ {0x8018, 170},
+ {0x801f, 170},
+ {0x8029, 170},
+ {0xc038, 170},
+ {0x8003, 173},
+ {0x8006, 173},
+ {0x800a, 173},
+ {0x800f, 173},
+ {0x8018, 173},
+ {0x801f, 173},
+ {0x8029, 173},
+ {0xc038, 173},
+ },
+ /* 133 */
+ {
+ {0x89, 0},
+ {0x8a, 0},
+ {0x8c, 0},
+ {0x8d, 0},
+ {0x90, 0},
+ {0x91, 0},
+ {0x93, 0},
+ {0x96, 0},
+ {0x9c, 0},
+ {0x9f, 0},
+ {0xa3, 0},
+ {0xa6, 0},
+ {0xab, 0},
+ {0xae, 0},
+ {0xb5, 0},
+ {0xbe, 0},
+ },
+ /* 134 */
+ {
+ {0xc000, 178},
+ {0xc000, 181},
+ {0xc000, 185},
+ {0xc000, 186},
+ {0xc000, 187},
+ {0xc000, 189},
+ {0xc000, 190},
+ {0xc000, 196},
+ {0xc000, 198},
+ {0xc000, 228},
+ {0xc000, 232},
+ {0xc000, 233},
+ {0x94, 0},
+ {0x95, 0},
+ {0x97, 0},
+ {0x98, 0},
+ },
+ /* 135 */
+ {
+ {0x8001, 178},
+ {0xc016, 178},
+ {0x8001, 181},
+ {0xc016, 181},
+ {0x8001, 185},
+ {0xc016, 185},
+ {0x8001, 186},
+ {0xc016, 186},
+ {0x8001, 187},
+ {0xc016, 187},
+ {0x8001, 189},
+ {0xc016, 189},
+ {0x8001, 190},
+ {0xc016, 190},
+ {0x8001, 196},
+ {0xc016, 196},
+ },
+ /* 136 */
+ {
+ {0x8002, 178},
+ {0x8009, 178},
+ {0x8017, 178},
+ {0xc028, 178},
+ {0x8002, 181},
+ {0x8009, 181},
+ {0x8017, 181},
+ {0xc028, 181},
+ {0x8002, 185},
+ {0x8009, 185},
+ {0x8017, 185},
+ {0xc028, 185},
+ {0x8002, 186},
+ {0x8009, 186},
+ {0x8017, 186},
+ {0xc028, 186},
+ },
+ /* 137 */
+ {
+ {0x8003, 178},
+ {0x8006, 178},
+ {0x800a, 178},
+ {0x800f, 178},
+ {0x8018, 178},
+ {0x801f, 178},
+ {0x8029, 178},
+ {0xc038, 178},
+ {0x8003, 181},
+ {0x8006, 181},
+ {0x800a, 181},
+ {0x800f, 181},
+ {0x8018, 181},
+ {0x801f, 181},
+ {0x8029, 181},
+ {0xc038, 181},
+ },
+ /* 138 */
+ {
+ {0x8003, 185},
+ {0x8006, 185},
+ {0x800a, 185},
+ {0x800f, 185},
+ {0x8018, 185},
+ {0x801f, 185},
+ {0x8029, 185},
+ {0xc038, 185},
+ {0x8003, 186},
+ {0x8006, 186},
+ {0x800a, 186},
+ {0x800f, 186},
+ {0x8018, 186},
+ {0x801f, 186},
+ {0x8029, 186},
+ {0xc038, 186},
+ },
+ /* 139 */
+ {
+ {0x8002, 187},
+ {0x8009, 187},
+ {0x8017, 187},
+ {0xc028, 187},
+ {0x8002, 189},
+ {0x8009, 189},
+ {0x8017, 189},
+ {0xc028, 189},
+ {0x8002, 190},
+ {0x8009, 190},
+ {0x8017, 190},
+ {0xc028, 190},
+ {0x8002, 196},
+ {0x8009, 196},
+ {0x8017, 196},
+ {0xc028, 196},
+ },
+ /* 140 */
+ {
+ {0x8003, 187},
+ {0x8006, 187},
+ {0x800a, 187},
+ {0x800f, 187},
+ {0x8018, 187},
+ {0x801f, 187},
+ {0x8029, 187},
+ {0xc038, 187},
+ {0x8003, 189},
+ {0x8006, 189},
+ {0x800a, 189},
+ {0x800f, 189},
+ {0x8018, 189},
+ {0x801f, 189},
+ {0x8029, 189},
+ {0xc038, 189},
+ },
+ /* 141 */
+ {
+ {0x8003, 190},
+ {0x8006, 190},
+ {0x800a, 190},
+ {0x800f, 190},
+ {0x8018, 190},
+ {0x801f, 190},
+ {0x8029, 190},
+ {0xc038, 190},
+ {0x8003, 196},
+ {0x8006, 196},
+ {0x800a, 196},
+ {0x800f, 196},
+ {0x8018, 196},
+ {0x801f, 196},
+ {0x8029, 196},
+ {0xc038, 196},
+ },
+ /* 142 */
+ {
+ {0x8001, 198},
+ {0xc016, 198},
+ {0x8001, 228},
+ {0xc016, 228},
+ {0x8001, 232},
+ {0xc016, 232},
+ {0x8001, 233},
+ {0xc016, 233},
+ {0xc000, 1},
+ {0xc000, 135},
+ {0xc000, 137},
+ {0xc000, 138},
+ {0xc000, 139},
+ {0xc000, 140},
+ {0xc000, 141},
+ {0xc000, 143},
+ },
+ /* 143 */
+ {
+ {0x8002, 198},
+ {0x8009, 198},
+ {0x8017, 198},
+ {0xc028, 198},
+ {0x8002, 228},
+ {0x8009, 228},
+ {0x8017, 228},
+ {0xc028, 228},
+ {0x8002, 232},
+ {0x8009, 232},
+ {0x8017, 232},
+ {0xc028, 232},
+ {0x8002, 233},
+ {0x8009, 233},
+ {0x8017, 233},
+ {0xc028, 233},
+ },
+ /* 144 */
+ {
+ {0x8003, 198},
+ {0x8006, 198},
+ {0x800a, 198},
+ {0x800f, 198},
+ {0x8018, 198},
+ {0x801f, 198},
+ {0x8029, 198},
+ {0xc038, 198},
+ {0x8003, 228},
+ {0x8006, 228},
+ {0x800a, 228},
+ {0x800f, 228},
+ {0x8018, 228},
+ {0x801f, 228},
+ {0x8029, 228},
+ {0xc038, 228},
+ },
+ /* 145 */
+ {
+ {0x8003, 232},
+ {0x8006, 232},
+ {0x800a, 232},
+ {0x800f, 232},
+ {0x8018, 232},
+ {0x801f, 232},
+ {0x8029, 232},
+ {0xc038, 232},
+ {0x8003, 233},
+ {0x8006, 233},
+ {0x800a, 233},
+ {0x800f, 233},
+ {0x8018, 233},
+ {0x801f, 233},
+ {0x8029, 233},
+ {0xc038, 233},
+ },
+ /* 146 */
+ {
+ {0x8001, 1},
+ {0xc016, 1},
+ {0x8001, 135},
+ {0xc016, 135},
+ {0x8001, 137},
+ {0xc016, 137},
+ {0x8001, 138},
+ {0xc016, 138},
+ {0x8001, 139},
+ {0xc016, 139},
+ {0x8001, 140},
+ {0xc016, 140},
+ {0x8001, 141},
+ {0xc016, 141},
+ {0x8001, 143},
+ {0xc016, 143},
+ },
+ /* 147 */
+ {
+ {0x8002, 1},
+ {0x8009, 1},
+ {0x8017, 1},
+ {0xc028, 1},
+ {0x8002, 135},
+ {0x8009, 135},
+ {0x8017, 135},
+ {0xc028, 135},
+ {0x8002, 137},
+ {0x8009, 137},
+ {0x8017, 137},
+ {0xc028, 137},
+ {0x8002, 138},
+ {0x8009, 138},
+ {0x8017, 138},
+ {0xc028, 138},
+ },
+ /* 148 */
+ {
+ {0x8003, 1},
+ {0x8006, 1},
+ {0x800a, 1},
+ {0x800f, 1},
+ {0x8018, 1},
+ {0x801f, 1},
+ {0x8029, 1},
+ {0xc038, 1},
+ {0x8003, 135},
+ {0x8006, 135},
+ {0x800a, 135},
+ {0x800f, 135},
+ {0x8018, 135},
+ {0x801f, 135},
+ {0x8029, 135},
+ {0xc038, 135},
+ },
+ /* 149 */
+ {
+ {0x8003, 137},
+ {0x8006, 137},
+ {0x800a, 137},
+ {0x800f, 137},
+ {0x8018, 137},
+ {0x801f, 137},
+ {0x8029, 137},
+ {0xc038, 137},
+ {0x8003, 138},
+ {0x8006, 138},
+ {0x800a, 138},
+ {0x800f, 138},
+ {0x8018, 138},
+ {0x801f, 138},
+ {0x8029, 138},
+ {0xc038, 138},
+ },
+ /* 150 */
+ {
+ {0x8002, 139},
+ {0x8009, 139},
+ {0x8017, 139},
+ {0xc028, 139},
+ {0x8002, 140},
+ {0x8009, 140},
+ {0x8017, 140},
+ {0xc028, 140},
+ {0x8002, 141},
+ {0x8009, 141},
+ {0x8017, 141},
+ {0xc028, 141},
+ {0x8002, 143},
+ {0x8009, 143},
+ {0x8017, 143},
+ {0xc028, 143},
+ },
+ /* 151 */
+ {
+ {0x8003, 139},
+ {0x8006, 139},
+ {0x800a, 139},
+ {0x800f, 139},
+ {0x8018, 139},
+ {0x801f, 139},
+ {0x8029, 139},
+ {0xc038, 139},
+ {0x8003, 140},
+ {0x8006, 140},
+ {0x800a, 140},
+ {0x800f, 140},
+ {0x8018, 140},
+ {0x801f, 140},
+ {0x8029, 140},
+ {0xc038, 140},
+ },
+ /* 152 */
+ {
+ {0x8003, 141},
+ {0x8006, 141},
+ {0x800a, 141},
+ {0x800f, 141},
+ {0x8018, 141},
+ {0x801f, 141},
+ {0x8029, 141},
+ {0xc038, 141},
+ {0x8003, 143},
+ {0x8006, 143},
+ {0x800a, 143},
+ {0x800f, 143},
+ {0x8018, 143},
+ {0x801f, 143},
+ {0x8029, 143},
+ {0xc038, 143},
+ },
+ /* 153 */
+ {
+ {0x9d, 0},
+ {0x9e, 0},
+ {0xa0, 0},
+ {0xa1, 0},
+ {0xa4, 0},
+ {0xa5, 0},
+ {0xa7, 0},
+ {0xa8, 0},
+ {0xac, 0},
+ {0xad, 0},
+ {0xaf, 0},
+ {0xb1, 0},
+ {0xb6, 0},
+ {0xb9, 0},
+ {0xbf, 0},
+ {0xcf, 0},
+ },
+ /* 154 */
+ {
+ {0xc000, 147},
+ {0xc000, 149},
+ {0xc000, 150},
+ {0xc000, 151},
+ {0xc000, 152},
+ {0xc000, 155},
+ {0xc000, 157},
+ {0xc000, 158},
+ {0xc000, 165},
+ {0xc000, 166},
+ {0xc000, 168},
+ {0xc000, 174},
+ {0xc000, 175},
+ {0xc000, 180},
+ {0xc000, 182},
+ {0xc000, 183},
+ },
+ /* 155 */
+ {
+ {0x8001, 147},
+ {0xc016, 147},
+ {0x8001, 149},
+ {0xc016, 149},
+ {0x8001, 150},
+ {0xc016, 150},
+ {0x8001, 151},
+ {0xc016, 151},
+ {0x8001, 152},
+ {0xc016, 152},
+ {0x8001, 155},
+ {0xc016, 155},
+ {0x8001, 157},
+ {0xc016, 157},
+ {0x8001, 158},
+ {0xc016, 158},
+ },
+ /* 156 */
+ {
+ {0x8002, 147},
+ {0x8009, 147},
+ {0x8017, 147},
+ {0xc028, 147},
+ {0x8002, 149},
+ {0x8009, 149},
+ {0x8017, 149},
+ {0xc028, 149},
+ {0x8002, 150},
+ {0x8009, 150},
+ {0x8017, 150},
+ {0xc028, 150},
+ {0x8002, 151},
+ {0x8009, 151},
+ {0x8017, 151},
+ {0xc028, 151},
+ },
+ /* 157 */
+ {
+ {0x8003, 147},
+ {0x8006, 147},
+ {0x800a, 147},
+ {0x800f, 147},
+ {0x8018, 147},
+ {0x801f, 147},
+ {0x8029, 147},
+ {0xc038, 147},
+ {0x8003, 149},
+ {0x8006, 149},
+ {0x800a, 149},
+ {0x800f, 149},
+ {0x8018, 149},
+ {0x801f, 149},
+ {0x8029, 149},
+ {0xc038, 149},
+ },
+ /* 158 */
+ {
+ {0x8003, 150},
+ {0x8006, 150},
+ {0x800a, 150},
+ {0x800f, 150},
+ {0x8018, 150},
+ {0x801f, 150},
+ {0x8029, 150},
+ {0xc038, 150},
+ {0x8003, 151},
+ {0x8006, 151},
+ {0x800a, 151},
+ {0x800f, 151},
+ {0x8018, 151},
+ {0x801f, 151},
+ {0x8029, 151},
+ {0xc038, 151},
+ },
+ /* 159 */
+ {
+ {0x8002, 152},
+ {0x8009, 152},
+ {0x8017, 152},
+ {0xc028, 152},
+ {0x8002, 155},
+ {0x8009, 155},
+ {0x8017, 155},
+ {0xc028, 155},
+ {0x8002, 157},
+ {0x8009, 157},
+ {0x8017, 157},
+ {0xc028, 157},
+ {0x8002, 158},
+ {0x8009, 158},
+ {0x8017, 158},
+ {0xc028, 158},
+ },
+ /* 160 */
+ {
+ {0x8003, 152},
+ {0x8006, 152},
+ {0x800a, 152},
+ {0x800f, 152},
+ {0x8018, 152},
+ {0x801f, 152},
+ {0x8029, 152},
+ {0xc038, 152},
+ {0x8003, 155},
+ {0x8006, 155},
+ {0x800a, 155},
+ {0x800f, 155},
+ {0x8018, 155},
+ {0x801f, 155},
+ {0x8029, 155},
+ {0xc038, 155},
+ },
+ /* 161 */
+ {
+ {0x8003, 157},
+ {0x8006, 157},
+ {0x800a, 157},
+ {0x800f, 157},
+ {0x8018, 157},
+ {0x801f, 157},
+ {0x8029, 157},
+ {0xc038, 157},
+ {0x8003, 158},
+ {0x8006, 158},
+ {0x800a, 158},
+ {0x800f, 158},
+ {0x8018, 158},
+ {0x801f, 158},
+ {0x8029, 158},
+ {0xc038, 158},
+ },
+ /* 162 */
+ {
+ {0x8001, 165},
+ {0xc016, 165},
+ {0x8001, 166},
+ {0xc016, 166},
+ {0x8001, 168},
+ {0xc016, 168},
+ {0x8001, 174},
+ {0xc016, 174},
+ {0x8001, 175},
+ {0xc016, 175},
+ {0x8001, 180},
+ {0xc016, 180},
+ {0x8001, 182},
+ {0xc016, 182},
+ {0x8001, 183},
+ {0xc016, 183},
+ },
+ /* 163 */
+ {
+ {0x8002, 165},
+ {0x8009, 165},
+ {0x8017, 165},
+ {0xc028, 165},
+ {0x8002, 166},
+ {0x8009, 166},
+ {0x8017, 166},
+ {0xc028, 166},
+ {0x8002, 168},
+ {0x8009, 168},
+ {0x8017, 168},
+ {0xc028, 168},
+ {0x8002, 174},
+ {0x8009, 174},
+ {0x8017, 174},
+ {0xc028, 174},
+ },
+ /* 164 */
+ {
+ {0x8003, 165},
+ {0x8006, 165},
+ {0x800a, 165},
+ {0x800f, 165},
+ {0x8018, 165},
+ {0x801f, 165},
+ {0x8029, 165},
+ {0xc038, 165},
+ {0x8003, 166},
+ {0x8006, 166},
+ {0x800a, 166},
+ {0x800f, 166},
+ {0x8018, 166},
+ {0x801f, 166},
+ {0x8029, 166},
+ {0xc038, 166},
+ },
+ /* 165 */
+ {
+ {0x8003, 168},
+ {0x8006, 168},
+ {0x800a, 168},
+ {0x800f, 168},
+ {0x8018, 168},
+ {0x801f, 168},
+ {0x8029, 168},
+ {0xc038, 168},
+ {0x8003, 174},
+ {0x8006, 174},
+ {0x800a, 174},
+ {0x800f, 174},
+ {0x8018, 174},
+ {0x801f, 174},
+ {0x8029, 174},
+ {0xc038, 174},
+ },
+ /* 166 */
+ {
+ {0x8002, 175},
+ {0x8009, 175},
+ {0x8017, 175},
+ {0xc028, 175},
+ {0x8002, 180},
+ {0x8009, 180},
+ {0x8017, 180},
+ {0xc028, 180},
+ {0x8002, 182},
+ {0x8009, 182},
+ {0x8017, 182},
+ {0xc028, 182},
+ {0x8002, 183},
+ {0x8009, 183},
+ {0x8017, 183},
+ {0xc028, 183},
+ },
+ /* 167 */
+ {
+ {0x8003, 175},
+ {0x8006, 175},
+ {0x800a, 175},
+ {0x800f, 175},
+ {0x8018, 175},
+ {0x801f, 175},
+ {0x8029, 175},
+ {0xc038, 175},
+ {0x8003, 180},
+ {0x8006, 180},
+ {0x800a, 180},
+ {0x800f, 180},
+ {0x8018, 180},
+ {0x801f, 180},
+ {0x8029, 180},
+ {0xc038, 180},
+ },
+ /* 168 */
+ {
+ {0x8003, 182},
+ {0x8006, 182},
+ {0x800a, 182},
+ {0x800f, 182},
+ {0x8018, 182},
+ {0x801f, 182},
+ {0x8029, 182},
+ {0xc038, 182},
+ {0x8003, 183},
+ {0x8006, 183},
+ {0x800a, 183},
+ {0x800f, 183},
+ {0x8018, 183},
+ {0x801f, 183},
+ {0x8029, 183},
+ {0xc038, 183},
+ },
+ /* 169 */
+ {
+ {0xc000, 188},
+ {0xc000, 191},
+ {0xc000, 197},
+ {0xc000, 231},
+ {0xc000, 239},
+ {0xb0, 0},
+ {0xb2, 0},
+ {0xb3, 0},
+ {0xb7, 0},
+ {0xb8, 0},
+ {0xba, 0},
+ {0xbb, 0},
+ {0xc0, 0},
+ {0xc7, 0},
+ {0xd0, 0},
+ {0xdf, 0},
+ },
+ /* 170 */
+ {
+ {0x8001, 188},
+ {0xc016, 188},
+ {0x8001, 191},
+ {0xc016, 191},
+ {0x8001, 197},
+ {0xc016, 197},
+ {0x8001, 231},
+ {0xc016, 231},
+ {0x8001, 239},
+ {0xc016, 239},
+ {0xc000, 9},
+ {0xc000, 142},
+ {0xc000, 144},
+ {0xc000, 145},
+ {0xc000, 148},
+ {0xc000, 159},
+ },
+ /* 171 */
+ {
+ {0x8002, 188},
+ {0x8009, 188},
+ {0x8017, 188},
+ {0xc028, 188},
+ {0x8002, 191},
+ {0x8009, 191},
+ {0x8017, 191},
+ {0xc028, 191},
+ {0x8002, 197},
+ {0x8009, 197},
+ {0x8017, 197},
+ {0xc028, 197},
+ {0x8002, 231},
+ {0x8009, 231},
+ {0x8017, 231},
+ {0xc028, 231},
+ },
+ /* 172 */
+ {
+ {0x8003, 188},
+ {0x8006, 188},
+ {0x800a, 188},
+ {0x800f, 188},
+ {0x8018, 188},
+ {0x801f, 188},
+ {0x8029, 188},
+ {0xc038, 188},
+ {0x8003, 191},
+ {0x8006, 191},
+ {0x800a, 191},
+ {0x800f, 191},
+ {0x8018, 191},
+ {0x801f, 191},
+ {0x8029, 191},
+ {0xc038, 191},
+ },
+ /* 173 */
+ {
+ {0x8003, 197},
+ {0x8006, 197},
+ {0x800a, 197},
+ {0x800f, 197},
+ {0x8018, 197},
+ {0x801f, 197},
+ {0x8029, 197},
+ {0xc038, 197},
+ {0x8003, 231},
+ {0x8006, 231},
+ {0x800a, 231},
+ {0x800f, 231},
+ {0x8018, 231},
+ {0x801f, 231},
+ {0x8029, 231},
+ {0xc038, 231},
+ },
+ /* 174 */
+ {
+ {0x8002, 239},
+ {0x8009, 239},
+ {0x8017, 239},
+ {0xc028, 239},
+ {0x8001, 9},
+ {0xc016, 9},
+ {0x8001, 142},
+ {0xc016, 142},
+ {0x8001, 144},
+ {0xc016, 144},
+ {0x8001, 145},
+ {0xc016, 145},
+ {0x8001, 148},
+ {0xc016, 148},
+ {0x8001, 159},
+ {0xc016, 159},
+ },
+ /* 175 */
+ {
+ {0x8003, 239},
+ {0x8006, 239},
+ {0x800a, 239},
+ {0x800f, 239},
+ {0x8018, 239},
+ {0x801f, 239},
+ {0x8029, 239},
+ {0xc038, 239},
+ {0x8002, 9},
+ {0x8009, 9},
+ {0x8017, 9},
+ {0xc028, 9},
+ {0x8002, 142},
+ {0x8009, 142},
+ {0x8017, 142},
+ {0xc028, 142},
+ },
+ /* 176 */
+ {
+ {0x8003, 9},
+ {0x8006, 9},
+ {0x800a, 9},
+ {0x800f, 9},
+ {0x8018, 9},
+ {0x801f, 9},
+ {0x8029, 9},
+ {0xc038, 9},
+ {0x8003, 142},
+ {0x8006, 142},
+ {0x800a, 142},
+ {0x800f, 142},
+ {0x8018, 142},
+ {0x801f, 142},
+ {0x8029, 142},
+ {0xc038, 142},
+ },
+ /* 177 */
+ {
+ {0x8002, 144},
+ {0x8009, 144},
+ {0x8017, 144},
+ {0xc028, 144},
+ {0x8002, 145},
+ {0x8009, 145},
+ {0x8017, 145},
+ {0xc028, 145},
+ {0x8002, 148},
+ {0x8009, 148},
+ {0x8017, 148},
+ {0xc028, 148},
+ {0x8002, 159},
+ {0x8009, 159},
+ {0x8017, 159},
+ {0xc028, 159},
+ },
+ /* 178 */
+ {
+ {0x8003, 144},
+ {0x8006, 144},
+ {0x800a, 144},
+ {0x800f, 144},
+ {0x8018, 144},
+ {0x801f, 144},
+ {0x8029, 144},
+ {0xc038, 144},
+ {0x8003, 145},
+ {0x8006, 145},
+ {0x800a, 145},
+ {0x800f, 145},
+ {0x8018, 145},
+ {0x801f, 145},
+ {0x8029, 145},
+ {0xc038, 145},
+ },
+ /* 179 */
+ {
+ {0x8003, 148},
+ {0x8006, 148},
+ {0x800a, 148},
+ {0x800f, 148},
+ {0x8018, 148},
+ {0x801f, 148},
+ {0x8029, 148},
+ {0xc038, 148},
+ {0x8003, 159},
+ {0x8006, 159},
+ {0x800a, 159},
+ {0x800f, 159},
+ {0x8018, 159},
+ {0x801f, 159},
+ {0x8029, 159},
+ {0xc038, 159},
+ },
+ /* 180 */
+ {
+ {0xc000, 171},
+ {0xc000, 206},
+ {0xc000, 215},
+ {0xc000, 225},
+ {0xc000, 236},
+ {0xc000, 237},
+ {0xbc, 0},
+ {0xbd, 0},
+ {0xc1, 0},
+ {0xc4, 0},
+ {0xc8, 0},
+ {0xcb, 0},
+ {0xd1, 0},
+ {0xd8, 0},
+ {0xe0, 0},
+ {0xee, 0},
+ },
+ /* 181 */
+ {
+ {0x8001, 171},
+ {0xc016, 171},
+ {0x8001, 206},
+ {0xc016, 206},
+ {0x8001, 215},
+ {0xc016, 215},
+ {0x8001, 225},
+ {0xc016, 225},
+ {0x8001, 236},
+ {0xc016, 236},
+ {0x8001, 237},
+ {0xc016, 237},
+ {0xc000, 199},
+ {0xc000, 207},
+ {0xc000, 234},
+ {0xc000, 235},
+ },
+ /* 182 */
+ {
+ {0x8002, 171},
+ {0x8009, 171},
+ {0x8017, 171},
+ {0xc028, 171},
+ {0x8002, 206},
+ {0x8009, 206},
+ {0x8017, 206},
+ {0xc028, 206},
+ {0x8002, 215},
+ {0x8009, 215},
+ {0x8017, 215},
+ {0xc028, 215},
+ {0x8002, 225},
+ {0x8009, 225},
+ {0x8017, 225},
+ {0xc028, 225},
+ },
+ /* 183 */
+ {
+ {0x8003, 171},
+ {0x8006, 171},
+ {0x800a, 171},
+ {0x800f, 171},
+ {0x8018, 171},
+ {0x801f, 171},
+ {0x8029, 171},
+ {0xc038, 171},
+ {0x8003, 206},
+ {0x8006, 206},
+ {0x800a, 206},
+ {0x800f, 206},
+ {0x8018, 206},
+ {0x801f, 206},
+ {0x8029, 206},
+ {0xc038, 206},
+ },
+ /* 184 */
+ {
+ {0x8003, 215},
+ {0x8006, 215},
+ {0x800a, 215},
+ {0x800f, 215},
+ {0x8018, 215},
+ {0x801f, 215},
+ {0x8029, 215},
+ {0xc038, 215},
+ {0x8003, 225},
+ {0x8006, 225},
+ {0x800a, 225},
+ {0x800f, 225},
+ {0x8018, 225},
+ {0x801f, 225},
+ {0x8029, 225},
+ {0xc038, 225},
+ },
+ /* 185 */
+ {
+ {0x8002, 236},
+ {0x8009, 236},
+ {0x8017, 236},
+ {0xc028, 236},
+ {0x8002, 237},
+ {0x8009, 237},
+ {0x8017, 237},
+ {0xc028, 237},
+ {0x8001, 199},
+ {0xc016, 199},
+ {0x8001, 207},
+ {0xc016, 207},
+ {0x8001, 234},
+ {0xc016, 234},
+ {0x8001, 235},
+ {0xc016, 235},
+ },
+ /* 186 */
+ {
+ {0x8003, 236},
+ {0x8006, 236},
+ {0x800a, 236},
+ {0x800f, 236},
+ {0x8018, 236},
+ {0x801f, 236},
+ {0x8029, 236},
+ {0xc038, 236},
+ {0x8003, 237},
+ {0x8006, 237},
+ {0x800a, 237},
+ {0x800f, 237},
+ {0x8018, 237},
+ {0x801f, 237},
+ {0x8029, 237},
+ {0xc038, 237},
+ },
+ /* 187 */
+ {
+ {0x8002, 199},
+ {0x8009, 199},
+ {0x8017, 199},
+ {0xc028, 199},
+ {0x8002, 207},
+ {0x8009, 207},
+ {0x8017, 207},
+ {0xc028, 207},
+ {0x8002, 234},
+ {0x8009, 234},
+ {0x8017, 234},
+ {0xc028, 234},
+ {0x8002, 235},
+ {0x8009, 235},
+ {0x8017, 235},
+ {0xc028, 235},
+ },
+ /* 188 */
+ {
+ {0x8003, 199},
+ {0x8006, 199},
+ {0x800a, 199},
+ {0x800f, 199},
+ {0x8018, 199},
+ {0x801f, 199},
+ {0x8029, 199},
+ {0xc038, 199},
+ {0x8003, 207},
+ {0x8006, 207},
+ {0x800a, 207},
+ {0x800f, 207},
+ {0x8018, 207},
+ {0x801f, 207},
+ {0x8029, 207},
+ {0xc038, 207},
+ },
+ /* 189 */
+ {
+ {0x8003, 234},
+ {0x8006, 234},
+ {0x800a, 234},
+ {0x800f, 234},
+ {0x8018, 234},
+ {0x801f, 234},
+ {0x8029, 234},
+ {0xc038, 234},
+ {0x8003, 235},
+ {0x8006, 235},
+ {0x800a, 235},
+ {0x800f, 235},
+ {0x8018, 235},
+ {0x801f, 235},
+ {0x8029, 235},
+ {0xc038, 235},
+ },
+ /* 190 */
+ {
+ {0xc2, 0},
+ {0xc3, 0},
+ {0xc5, 0},
+ {0xc6, 0},
+ {0xc9, 0},
+ {0xca, 0},
+ {0xcc, 0},
+ {0xcd, 0},
+ {0xd2, 0},
+ {0xd5, 0},
+ {0xd9, 0},
+ {0xdc, 0},
+ {0xe1, 0},
+ {0xe7, 0},
+ {0xef, 0},
+ {0xf6, 0},
+ },
+ /* 191 */
+ {
+ {0xc000, 192},
+ {0xc000, 193},
+ {0xc000, 200},
+ {0xc000, 201},
+ {0xc000, 202},
+ {0xc000, 205},
+ {0xc000, 210},
+ {0xc000, 213},
+ {0xc000, 218},
+ {0xc000, 219},
+ {0xc000, 238},
+ {0xc000, 240},
+ {0xc000, 242},
+ {0xc000, 243},
+ {0xc000, 255},
+ {0xce, 0},
+ },
+ /* 192 */
+ {
+ {0x8001, 192},
+ {0xc016, 192},
+ {0x8001, 193},
+ {0xc016, 193},
+ {0x8001, 200},
+ {0xc016, 200},
+ {0x8001, 201},
+ {0xc016, 201},
+ {0x8001, 202},
+ {0xc016, 202},
+ {0x8001, 205},
+ {0xc016, 205},
+ {0x8001, 210},
+ {0xc016, 210},
+ {0x8001, 213},
+ {0xc016, 213},
+ },
+ /* 193 */
+ {
+ {0x8002, 192},
+ {0x8009, 192},
+ {0x8017, 192},
+ {0xc028, 192},
+ {0x8002, 193},
+ {0x8009, 193},
+ {0x8017, 193},
+ {0xc028, 193},
+ {0x8002, 200},
+ {0x8009, 200},
+ {0x8017, 200},
+ {0xc028, 200},
+ {0x8002, 201},
+ {0x8009, 201},
+ {0x8017, 201},
+ {0xc028, 201},
+ },
+ /* 194 */
+ {
+ {0x8003, 192},
+ {0x8006, 192},
+ {0x800a, 192},
+ {0x800f, 192},
+ {0x8018, 192},
+ {0x801f, 192},
+ {0x8029, 192},
+ {0xc038, 192},
+ {0x8003, 193},
+ {0x8006, 193},
+ {0x800a, 193},
+ {0x800f, 193},
+ {0x8018, 193},
+ {0x801f, 193},
+ {0x8029, 193},
+ {0xc038, 193},
+ },
+ /* 195 */
+ {
+ {0x8003, 200},
+ {0x8006, 200},
+ {0x800a, 200},
+ {0x800f, 200},
+ {0x8018, 200},
+ {0x801f, 200},
+ {0x8029, 200},
+ {0xc038, 200},
+ {0x8003, 201},
+ {0x8006, 201},
+ {0x800a, 201},
+ {0x800f, 201},
+ {0x8018, 201},
+ {0x801f, 201},
+ {0x8029, 201},
+ {0xc038, 201},
+ },
+ /* 196 */
+ {
+ {0x8002, 202},
+ {0x8009, 202},
+ {0x8017, 202},
+ {0xc028, 202},
+ {0x8002, 205},
+ {0x8009, 205},
+ {0x8017, 205},
+ {0xc028, 205},
+ {0x8002, 210},
+ {0x8009, 210},
+ {0x8017, 210},
+ {0xc028, 210},
+ {0x8002, 213},
+ {0x8009, 213},
+ {0x8017, 213},
+ {0xc028, 213},
+ },
+ /* 197 */
+ {
+ {0x8003, 202},
+ {0x8006, 202},
+ {0x800a, 202},
+ {0x800f, 202},
+ {0x8018, 202},
+ {0x801f, 202},
+ {0x8029, 202},
+ {0xc038, 202},
+ {0x8003, 205},
+ {0x8006, 205},
+ {0x800a, 205},
+ {0x800f, 205},
+ {0x8018, 205},
+ {0x801f, 205},
+ {0x8029, 205},
+ {0xc038, 205},
+ },
+ /* 198 */
+ {
+ {0x8003, 210},
+ {0x8006, 210},
+ {0x800a, 210},
+ {0x800f, 210},
+ {0x8018, 210},
+ {0x801f, 210},
+ {0x8029, 210},
+ {0xc038, 210},
+ {0x8003, 213},
+ {0x8006, 213},
+ {0x800a, 213},
+ {0x800f, 213},
+ {0x8018, 213},
+ {0x801f, 213},
+ {0x8029, 213},
+ {0xc038, 213},
+ },
+ /* 199 */
+ {
+ {0x8001, 218},
+ {0xc016, 218},
+ {0x8001, 219},
+ {0xc016, 219},
+ {0x8001, 238},
+ {0xc016, 238},
+ {0x8001, 240},
+ {0xc016, 240},
+ {0x8001, 242},
+ {0xc016, 242},
+ {0x8001, 243},
+ {0xc016, 243},
+ {0x8001, 255},
+ {0xc016, 255},
+ {0xc000, 203},
+ {0xc000, 204},
+ },
+ /* 200 */
+ {
+ {0x8002, 218},
+ {0x8009, 218},
+ {0x8017, 218},
+ {0xc028, 218},
+ {0x8002, 219},
+ {0x8009, 219},
+ {0x8017, 219},
+ {0xc028, 219},
+ {0x8002, 238},
+ {0x8009, 238},
+ {0x8017, 238},
+ {0xc028, 238},
+ {0x8002, 240},
+ {0x8009, 240},
+ {0x8017, 240},
+ {0xc028, 240},
+ },
+ /* 201 */
+ {
+ {0x8003, 218},
+ {0x8006, 218},
+ {0x800a, 218},
+ {0x800f, 218},
+ {0x8018, 218},
+ {0x801f, 218},
+ {0x8029, 218},
+ {0xc038, 218},
+ {0x8003, 219},
+ {0x8006, 219},
+ {0x800a, 219},
+ {0x800f, 219},
+ {0x8018, 219},
+ {0x801f, 219},
+ {0x8029, 219},
+ {0xc038, 219},
+ },
+ /* 202 */
+ {
+ {0x8003, 238},
+ {0x8006, 238},
+ {0x800a, 238},
+ {0x800f, 238},
+ {0x8018, 238},
+ {0x801f, 238},
+ {0x8029, 238},
+ {0xc038, 238},
+ {0x8003, 240},
+ {0x8006, 240},
+ {0x800a, 240},
+ {0x800f, 240},
+ {0x8018, 240},
+ {0x801f, 240},
+ {0x8029, 240},
+ {0xc038, 240},
+ },
+ /* 203 */
+ {
+ {0x8002, 242},
+ {0x8009, 242},
+ {0x8017, 242},
+ {0xc028, 242},
+ {0x8002, 243},
+ {0x8009, 243},
+ {0x8017, 243},
+ {0xc028, 243},
+ {0x8002, 255},
+ {0x8009, 255},
+ {0x8017, 255},
+ {0xc028, 255},
+ {0x8001, 203},
+ {0xc016, 203},
+ {0x8001, 204},
+ {0xc016, 204},
+ },
+ /* 204 */
+ {
+ {0x8003, 242},
+ {0x8006, 242},
+ {0x800a, 242},
+ {0x800f, 242},
+ {0x8018, 242},
+ {0x801f, 242},
+ {0x8029, 242},
+ {0xc038, 242},
+ {0x8003, 243},
+ {0x8006, 243},
+ {0x800a, 243},
+ {0x800f, 243},
+ {0x8018, 243},
+ {0x801f, 243},
+ {0x8029, 243},
+ {0xc038, 243},
+ },
+ /* 205 */
+ {
+ {0x8003, 255},
+ {0x8006, 255},
+ {0x800a, 255},
+ {0x800f, 255},
+ {0x8018, 255},
+ {0x801f, 255},
+ {0x8029, 255},
+ {0xc038, 255},
+ {0x8002, 203},
+ {0x8009, 203},
+ {0x8017, 203},
+ {0xc028, 203},
+ {0x8002, 204},
+ {0x8009, 204},
+ {0x8017, 204},
+ {0xc028, 204},
+ },
+ /* 206 */
+ {
+ {0x8003, 203},
+ {0x8006, 203},
+ {0x800a, 203},
+ {0x800f, 203},
+ {0x8018, 203},
+ {0x801f, 203},
+ {0x8029, 203},
+ {0xc038, 203},
+ {0x8003, 204},
+ {0x8006, 204},
+ {0x800a, 204},
+ {0x800f, 204},
+ {0x8018, 204},
+ {0x801f, 204},
+ {0x8029, 204},
+ {0xc038, 204},
+ },
+ /* 207 */
+ {
+ {0xd3, 0},
+ {0xd4, 0},
+ {0xd6, 0},
+ {0xd7, 0},
+ {0xda, 0},
+ {0xdb, 0},
+ {0xdd, 0},
+ {0xde, 0},
+ {0xe2, 0},
+ {0xe4, 0},
+ {0xe8, 0},
+ {0xeb, 0},
+ {0xf0, 0},
+ {0xf3, 0},
+ {0xf7, 0},
+ {0xfa, 0},
+ },
+ /* 208 */
+ {
+ {0xc000, 211},
+ {0xc000, 212},
+ {0xc000, 214},
+ {0xc000, 221},
+ {0xc000, 222},
+ {0xc000, 223},
+ {0xc000, 241},
+ {0xc000, 244},
+ {0xc000, 245},
+ {0xc000, 246},
+ {0xc000, 247},
+ {0xc000, 248},
+ {0xc000, 250},
+ {0xc000, 251},
+ {0xc000, 252},
+ {0xc000, 253},
+ },
+ /* 209 */
+ {
+ {0x8001, 211},
+ {0xc016, 211},
+ {0x8001, 212},
+ {0xc016, 212},
+ {0x8001, 214},
+ {0xc016, 214},
+ {0x8001, 221},
+ {0xc016, 221},
+ {0x8001, 222},
+ {0xc016, 222},
+ {0x8001, 223},
+ {0xc016, 223},
+ {0x8001, 241},
+ {0xc016, 241},
+ {0x8001, 244},
+ {0xc016, 244},
+ },
+ /* 210 */
+ {
+ {0x8002, 211},
+ {0x8009, 211},
+ {0x8017, 211},
+ {0xc028, 211},
+ {0x8002, 212},
+ {0x8009, 212},
+ {0x8017, 212},
+ {0xc028, 212},
+ {0x8002, 214},
+ {0x8009, 214},
+ {0x8017, 214},
+ {0xc028, 214},
+ {0x8002, 221},
+ {0x8009, 221},
+ {0x8017, 221},
+ {0xc028, 221},
+ },
+ /* 211 */
+ {
+ {0x8003, 211},
+ {0x8006, 211},
+ {0x800a, 211},
+ {0x800f, 211},
+ {0x8018, 211},
+ {0x801f, 211},
+ {0x8029, 211},
+ {0xc038, 211},
+ {0x8003, 212},
+ {0x8006, 212},
+ {0x800a, 212},
+ {0x800f, 212},
+ {0x8018, 212},
+ {0x801f, 212},
+ {0x8029, 212},
+ {0xc038, 212},
+ },
+ /* 212 */
+ {
+ {0x8003, 214},
+ {0x8006, 214},
+ {0x800a, 214},
+ {0x800f, 214},
+ {0x8018, 214},
+ {0x801f, 214},
+ {0x8029, 214},
+ {0xc038, 214},
+ {0x8003, 221},
+ {0x8006, 221},
+ {0x800a, 221},
+ {0x800f, 221},
+ {0x8018, 221},
+ {0x801f, 221},
+ {0x8029, 221},
+ {0xc038, 221},
+ },
+ /* 213 */
+ {
+ {0x8002, 222},
+ {0x8009, 222},
+ {0x8017, 222},
+ {0xc028, 222},
+ {0x8002, 223},
+ {0x8009, 223},
+ {0x8017, 223},
+ {0xc028, 223},
+ {0x8002, 241},
+ {0x8009, 241},
+ {0x8017, 241},
+ {0xc028, 241},
+ {0x8002, 244},
+ {0x8009, 244},
+ {0x8017, 244},
+ {0xc028, 244},
+ },
+ /* 214 */
+ {
+ {0x8003, 222},
+ {0x8006, 222},
+ {0x800a, 222},
+ {0x800f, 222},
+ {0x8018, 222},
+ {0x801f, 222},
+ {0x8029, 222},
+ {0xc038, 222},
+ {0x8003, 223},
+ {0x8006, 223},
+ {0x800a, 223},
+ {0x800f, 223},
+ {0x8018, 223},
+ {0x801f, 223},
+ {0x8029, 223},
+ {0xc038, 223},
+ },
+ /* 215 */
+ {
+ {0x8003, 241},
+ {0x8006, 241},
+ {0x800a, 241},
+ {0x800f, 241},
+ {0x8018, 241},
+ {0x801f, 241},
+ {0x8029, 241},
+ {0xc038, 241},
+ {0x8003, 244},
+ {0x8006, 244},
+ {0x800a, 244},
+ {0x800f, 244},
+ {0x8018, 244},
+ {0x801f, 244},
+ {0x8029, 244},
+ {0xc038, 244},
+ },
+ /* 216 */
+ {
+ {0x8001, 245},
+ {0xc016, 245},
+ {0x8001, 246},
+ {0xc016, 246},
+ {0x8001, 247},
+ {0xc016, 247},
+ {0x8001, 248},
+ {0xc016, 248},
+ {0x8001, 250},
+ {0xc016, 250},
+ {0x8001, 251},
+ {0xc016, 251},
+ {0x8001, 252},
+ {0xc016, 252},
+ {0x8001, 253},
+ {0xc016, 253},
+ },
+ /* 217 */
+ {
+ {0x8002, 245},
+ {0x8009, 245},
+ {0x8017, 245},
+ {0xc028, 245},
+ {0x8002, 246},
+ {0x8009, 246},
+ {0x8017, 246},
+ {0xc028, 246},
+ {0x8002, 247},
+ {0x8009, 247},
+ {0x8017, 247},
+ {0xc028, 247},
+ {0x8002, 248},
+ {0x8009, 248},
+ {0x8017, 248},
+ {0xc028, 248},
+ },
+ /* 218 */
+ {
+ {0x8003, 245},
+ {0x8006, 245},
+ {0x800a, 245},
+ {0x800f, 245},
+ {0x8018, 245},
+ {0x801f, 245},
+ {0x8029, 245},
+ {0xc038, 245},
+ {0x8003, 246},
+ {0x8006, 246},
+ {0x800a, 246},
+ {0x800f, 246},
+ {0x8018, 246},
+ {0x801f, 246},
+ {0x8029, 246},
+ {0xc038, 246},
+ },
+ /* 219 */
+ {
+ {0x8003, 247},
+ {0x8006, 247},
+ {0x800a, 247},
+ {0x800f, 247},
+ {0x8018, 247},
+ {0x801f, 247},
+ {0x8029, 247},
+ {0xc038, 247},
+ {0x8003, 248},
+ {0x8006, 248},
+ {0x800a, 248},
+ {0x800f, 248},
+ {0x8018, 248},
+ {0x801f, 248},
+ {0x8029, 248},
+ {0xc038, 248},
+ },
+ /* 220 */
+ {
+ {0x8002, 250},
+ {0x8009, 250},
+ {0x8017, 250},
+ {0xc028, 250},
+ {0x8002, 251},
+ {0x8009, 251},
+ {0x8017, 251},
+ {0xc028, 251},
+ {0x8002, 252},
+ {0x8009, 252},
+ {0x8017, 252},
+ {0xc028, 252},
+ {0x8002, 253},
+ {0x8009, 253},
+ {0x8017, 253},
+ {0xc028, 253},
+ },
+ /* 221 */
+ {
+ {0x8003, 250},
+ {0x8006, 250},
+ {0x800a, 250},
+ {0x800f, 250},
+ {0x8018, 250},
+ {0x801f, 250},
+ {0x8029, 250},
+ {0xc038, 250},
+ {0x8003, 251},
+ {0x8006, 251},
+ {0x800a, 251},
+ {0x800f, 251},
+ {0x8018, 251},
+ {0x801f, 251},
+ {0x8029, 251},
+ {0xc038, 251},
+ },
+ /* 222 */
+ {
+ {0x8003, 252},
+ {0x8006, 252},
+ {0x800a, 252},
+ {0x800f, 252},
+ {0x8018, 252},
+ {0x801f, 252},
+ {0x8029, 252},
+ {0xc038, 252},
+ {0x8003, 253},
+ {0x8006, 253},
+ {0x800a, 253},
+ {0x800f, 253},
+ {0x8018, 253},
+ {0x801f, 253},
+ {0x8029, 253},
+ {0xc038, 253},
+ },
+ /* 223 */
+ {
+ {0xc000, 254},
+ {0xe3, 0},
+ {0xe5, 0},
+ {0xe6, 0},
+ {0xe9, 0},
+ {0xea, 0},
+ {0xec, 0},
+ {0xed, 0},
+ {0xf1, 0},
+ {0xf2, 0},
+ {0xf4, 0},
+ {0xf5, 0},
+ {0xf8, 0},
+ {0xf9, 0},
+ {0xfb, 0},
+ {0xfc, 0},
+ },
+ /* 224 */
+ {
+ {0x8001, 254},
+ {0xc016, 254},
+ {0xc000, 2},
+ {0xc000, 3},
+ {0xc000, 4},
+ {0xc000, 5},
+ {0xc000, 6},
+ {0xc000, 7},
+ {0xc000, 8},
+ {0xc000, 11},
+ {0xc000, 12},
+ {0xc000, 14},
+ {0xc000, 15},
+ {0xc000, 16},
+ {0xc000, 17},
+ {0xc000, 18},
+ },
+ /* 225 */
+ {
+ {0x8002, 254},
+ {0x8009, 254},
+ {0x8017, 254},
+ {0xc028, 254},
+ {0x8001, 2},
+ {0xc016, 2},
+ {0x8001, 3},
+ {0xc016, 3},
+ {0x8001, 4},
+ {0xc016, 4},
+ {0x8001, 5},
+ {0xc016, 5},
+ {0x8001, 6},
+ {0xc016, 6},
+ {0x8001, 7},
+ {0xc016, 7},
+ },
+ /* 226 */
+ {
+ {0x8003, 254},
+ {0x8006, 254},
+ {0x800a, 254},
+ {0x800f, 254},
+ {0x8018, 254},
+ {0x801f, 254},
+ {0x8029, 254},
+ {0xc038, 254},
+ {0x8002, 2},
+ {0x8009, 2},
+ {0x8017, 2},
+ {0xc028, 2},
+ {0x8002, 3},
+ {0x8009, 3},
+ {0x8017, 3},
+ {0xc028, 3},
+ },
+ /* 227 */
+ {
+ {0x8003, 2},
+ {0x8006, 2},
+ {0x800a, 2},
+ {0x800f, 2},
+ {0x8018, 2},
+ {0x801f, 2},
+ {0x8029, 2},
+ {0xc038, 2},
+ {0x8003, 3},
+ {0x8006, 3},
+ {0x800a, 3},
+ {0x800f, 3},
+ {0x8018, 3},
+ {0x801f, 3},
+ {0x8029, 3},
+ {0xc038, 3},
+ },
+ /* 228 */
+ {
+ {0x8002, 4},
+ {0x8009, 4},
+ {0x8017, 4},
+ {0xc028, 4},
+ {0x8002, 5},
+ {0x8009, 5},
+ {0x8017, 5},
+ {0xc028, 5},
+ {0x8002, 6},
+ {0x8009, 6},
+ {0x8017, 6},
+ {0xc028, 6},
+ {0x8002, 7},
+ {0x8009, 7},
+ {0x8017, 7},
+ {0xc028, 7},
+ },
+ /* 229 */
+ {
+ {0x8003, 4},
+ {0x8006, 4},
+ {0x800a, 4},
+ {0x800f, 4},
+ {0x8018, 4},
+ {0x801f, 4},
+ {0x8029, 4},
+ {0xc038, 4},
+ {0x8003, 5},
+ {0x8006, 5},
+ {0x800a, 5},
+ {0x800f, 5},
+ {0x8018, 5},
+ {0x801f, 5},
+ {0x8029, 5},
+ {0xc038, 5},
+ },
+ /* 230 */
+ {
+ {0x8003, 6},
+ {0x8006, 6},
+ {0x800a, 6},
+ {0x800f, 6},
+ {0x8018, 6},
+ {0x801f, 6},
+ {0x8029, 6},
+ {0xc038, 6},
+ {0x8003, 7},
+ {0x8006, 7},
+ {0x800a, 7},
+ {0x800f, 7},
+ {0x8018, 7},
+ {0x801f, 7},
+ {0x8029, 7},
+ {0xc038, 7},
+ },
+ /* 231 */
+ {
+ {0x8001, 8},
+ {0xc016, 8},
+ {0x8001, 11},
+ {0xc016, 11},
+ {0x8001, 12},
+ {0xc016, 12},
+ {0x8001, 14},
+ {0xc016, 14},
+ {0x8001, 15},
+ {0xc016, 15},
+ {0x8001, 16},
+ {0xc016, 16},
+ {0x8001, 17},
+ {0xc016, 17},
+ {0x8001, 18},
+ {0xc016, 18},
+ },
+ /* 232 */
+ {
+ {0x8002, 8},
+ {0x8009, 8},
+ {0x8017, 8},
+ {0xc028, 8},
+ {0x8002, 11},
+ {0x8009, 11},
+ {0x8017, 11},
+ {0xc028, 11},
+ {0x8002, 12},
+ {0x8009, 12},
+ {0x8017, 12},
+ {0xc028, 12},
+ {0x8002, 14},
+ {0x8009, 14},
+ {0x8017, 14},
+ {0xc028, 14},
+ },
+ /* 233 */
+ {
+ {0x8003, 8},
+ {0x8006, 8},
+ {0x800a, 8},
+ {0x800f, 8},
+ {0x8018, 8},
+ {0x801f, 8},
+ {0x8029, 8},
+ {0xc038, 8},
+ {0x8003, 11},
+ {0x8006, 11},
+ {0x800a, 11},
+ {0x800f, 11},
+ {0x8018, 11},
+ {0x801f, 11},
+ {0x8029, 11},
+ {0xc038, 11},
+ },
+ /* 234 */
+ {
+ {0x8003, 12},
+ {0x8006, 12},
+ {0x800a, 12},
+ {0x800f, 12},
+ {0x8018, 12},
+ {0x801f, 12},
+ {0x8029, 12},
+ {0xc038, 12},
+ {0x8003, 14},
+ {0x8006, 14},
+ {0x800a, 14},
+ {0x800f, 14},
+ {0x8018, 14},
+ {0x801f, 14},
+ {0x8029, 14},
+ {0xc038, 14},
+ },
+ /* 235 */
+ {
+ {0x8002, 15},
+ {0x8009, 15},
+ {0x8017, 15},
+ {0xc028, 15},
+ {0x8002, 16},
+ {0x8009, 16},
+ {0x8017, 16},
+ {0xc028, 16},
+ {0x8002, 17},
+ {0x8009, 17},
+ {0x8017, 17},
+ {0xc028, 17},
+ {0x8002, 18},
+ {0x8009, 18},
+ {0x8017, 18},
+ {0xc028, 18},
+ },
+ /* 236 */
+ {
+ {0x8003, 15},
+ {0x8006, 15},
+ {0x800a, 15},
+ {0x800f, 15},
+ {0x8018, 15},
+ {0x801f, 15},
+ {0x8029, 15},
+ {0xc038, 15},
+ {0x8003, 16},
+ {0x8006, 16},
+ {0x800a, 16},
+ {0x800f, 16},
+ {0x8018, 16},
+ {0x801f, 16},
+ {0x8029, 16},
+ {0xc038, 16},
+ },
+ /* 237 */
+ {
+ {0x8003, 17},
+ {0x8006, 17},
+ {0x800a, 17},
+ {0x800f, 17},
+ {0x8018, 17},
+ {0x801f, 17},
+ {0x8029, 17},
+ {0xc038, 17},
+ {0x8003, 18},
+ {0x8006, 18},
+ {0x800a, 18},
+ {0x800f, 18},
+ {0x8018, 18},
+ {0x801f, 18},
+ {0x8029, 18},
+ {0xc038, 18},
+ },
+ /* 238 */
+ {
+ {0xc000, 19},
+ {0xc000, 20},
+ {0xc000, 21},
+ {0xc000, 23},
+ {0xc000, 24},
+ {0xc000, 25},
+ {0xc000, 26},
+ {0xc000, 27},
+ {0xc000, 28},
+ {0xc000, 29},
+ {0xc000, 30},
+ {0xc000, 31},
+ {0xc000, 127},
+ {0xc000, 220},
+ {0xc000, 249},
+ {0xfd, 0},
+ },
+ /* 239 */
+ {
+ {0x8001, 19},
+ {0xc016, 19},
+ {0x8001, 20},
+ {0xc016, 20},
+ {0x8001, 21},
+ {0xc016, 21},
+ {0x8001, 23},
+ {0xc016, 23},
+ {0x8001, 24},
+ {0xc016, 24},
+ {0x8001, 25},
+ {0xc016, 25},
+ {0x8001, 26},
+ {0xc016, 26},
+ {0x8001, 27},
+ {0xc016, 27},
+ },
+ /* 240 */
+ {
+ {0x8002, 19},
+ {0x8009, 19},
+ {0x8017, 19},
+ {0xc028, 19},
+ {0x8002, 20},
+ {0x8009, 20},
+ {0x8017, 20},
+ {0xc028, 20},
+ {0x8002, 21},
+ {0x8009, 21},
+ {0x8017, 21},
+ {0xc028, 21},
+ {0x8002, 23},
+ {0x8009, 23},
+ {0x8017, 23},
+ {0xc028, 23},
+ },
+ /* 241 */
+ {
+ {0x8003, 19},
+ {0x8006, 19},
+ {0x800a, 19},
+ {0x800f, 19},
+ {0x8018, 19},
+ {0x801f, 19},
+ {0x8029, 19},
+ {0xc038, 19},
+ {0x8003, 20},
+ {0x8006, 20},
+ {0x800a, 20},
+ {0x800f, 20},
+ {0x8018, 20},
+ {0x801f, 20},
+ {0x8029, 20},
+ {0xc038, 20},
+ },
+ /* 242 */
+ {
+ {0x8003, 21},
+ {0x8006, 21},
+ {0x800a, 21},
+ {0x800f, 21},
+ {0x8018, 21},
+ {0x801f, 21},
+ {0x8029, 21},
+ {0xc038, 21},
+ {0x8003, 23},
+ {0x8006, 23},
+ {0x800a, 23},
+ {0x800f, 23},
+ {0x8018, 23},
+ {0x801f, 23},
+ {0x8029, 23},
+ {0xc038, 23},
+ },
+ /* 243 */
+ {
+ {0x8002, 24},
+ {0x8009, 24},
+ {0x8017, 24},
+ {0xc028, 24},
+ {0x8002, 25},
+ {0x8009, 25},
+ {0x8017, 25},
+ {0xc028, 25},
+ {0x8002, 26},
+ {0x8009, 26},
+ {0x8017, 26},
+ {0xc028, 26},
+ {0x8002, 27},
+ {0x8009, 27},
+ {0x8017, 27},
+ {0xc028, 27},
+ },
+ /* 244 */
+ {
+ {0x8003, 24},
+ {0x8006, 24},
+ {0x800a, 24},
+ {0x800f, 24},
+ {0x8018, 24},
+ {0x801f, 24},
+ {0x8029, 24},
+ {0xc038, 24},
+ {0x8003, 25},
+ {0x8006, 25},
+ {0x800a, 25},
+ {0x800f, 25},
+ {0x8018, 25},
+ {0x801f, 25},
+ {0x8029, 25},
+ {0xc038, 25},
+ },
+ /* 245 */
+ {
+ {0x8003, 26},
+ {0x8006, 26},
+ {0x800a, 26},
+ {0x800f, 26},
+ {0x8018, 26},
+ {0x801f, 26},
+ {0x8029, 26},
+ {0xc038, 26},
+ {0x8003, 27},
+ {0x8006, 27},
+ {0x800a, 27},
+ {0x800f, 27},
+ {0x8018, 27},
+ {0x801f, 27},
+ {0x8029, 27},
+ {0xc038, 27},
+ },
+ /* 246 */
+ {
+ {0x8001, 28},
+ {0xc016, 28},
+ {0x8001, 29},
+ {0xc016, 29},
+ {0x8001, 30},
+ {0xc016, 30},
+ {0x8001, 31},
+ {0xc016, 31},
+ {0x8001, 127},
+ {0xc016, 127},
+ {0x8001, 220},
+ {0xc016, 220},
+ {0x8001, 249},
+ {0xc016, 249},
+ {0xfe, 0},
+ {0xff, 0},
+ },
+ /* 247 */
+ {
+ {0x8002, 28},
+ {0x8009, 28},
+ {0x8017, 28},
+ {0xc028, 28},
+ {0x8002, 29},
+ {0x8009, 29},
+ {0x8017, 29},
+ {0xc028, 29},
+ {0x8002, 30},
+ {0x8009, 30},
+ {0x8017, 30},
+ {0xc028, 30},
+ {0x8002, 31},
+ {0x8009, 31},
+ {0x8017, 31},
+ {0xc028, 31},
+ },
+ /* 248 */
+ {
+ {0x8003, 28},
+ {0x8006, 28},
+ {0x800a, 28},
+ {0x800f, 28},
+ {0x8018, 28},
+ {0x801f, 28},
+ {0x8029, 28},
+ {0xc038, 28},
+ {0x8003, 29},
+ {0x8006, 29},
+ {0x800a, 29},
+ {0x800f, 29},
+ {0x8018, 29},
+ {0x801f, 29},
+ {0x8029, 29},
+ {0xc038, 29},
+ },
+ /* 249 */
+ {
+ {0x8003, 30},
+ {0x8006, 30},
+ {0x800a, 30},
+ {0x800f, 30},
+ {0x8018, 30},
+ {0x801f, 30},
+ {0x8029, 30},
+ {0xc038, 30},
+ {0x8003, 31},
+ {0x8006, 31},
+ {0x800a, 31},
+ {0x800f, 31},
+ {0x8018, 31},
+ {0x801f, 31},
+ {0x8029, 31},
+ {0xc038, 31},
+ },
+ /* 250 */
+ {
+ {0x8002, 127},
+ {0x8009, 127},
+ {0x8017, 127},
+ {0xc028, 127},
+ {0x8002, 220},
+ {0x8009, 220},
+ {0x8017, 220},
+ {0xc028, 220},
+ {0x8002, 249},
+ {0x8009, 249},
+ {0x8017, 249},
+ {0xc028, 249},
+ {0xc000, 10},
+ {0xc000, 13},
+ {0xc000, 22},
+ {0x100, 0},
+ },
+ /* 251 */
+ {
+ {0x8003, 127},
+ {0x8006, 127},
+ {0x800a, 127},
+ {0x800f, 127},
+ {0x8018, 127},
+ {0x801f, 127},
+ {0x8029, 127},
+ {0xc038, 127},
+ {0x8003, 220},
+ {0x8006, 220},
+ {0x800a, 220},
+ {0x800f, 220},
+ {0x8018, 220},
+ {0x801f, 220},
+ {0x8029, 220},
+ {0xc038, 220},
+ },
+ /* 252 */
+ {
+ {0x8003, 249},
+ {0x8006, 249},
+ {0x800a, 249},
+ {0x800f, 249},
+ {0x8018, 249},
+ {0x801f, 249},
+ {0x8029, 249},
+ {0xc038, 249},
+ {0x8001, 10},
+ {0xc016, 10},
+ {0x8001, 13},
+ {0xc016, 13},
+ {0x8001, 22},
+ {0xc016, 22},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 253 */
+ {
+ {0x8002, 10},
+ {0x8009, 10},
+ {0x8017, 10},
+ {0xc028, 10},
+ {0x8002, 13},
+ {0x8009, 13},
+ {0x8017, 13},
+ {0xc028, 13},
+ {0x8002, 22},
+ {0x8009, 22},
+ {0x8017, 22},
+ {0xc028, 22},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 254 */
+ {
+ {0x8003, 10},
+ {0x8006, 10},
+ {0x800a, 10},
+ {0x800f, 10},
+ {0x8018, 10},
+ {0x801f, 10},
+ {0x8029, 10},
+ {0xc038, 10},
+ {0x8003, 13},
+ {0x8006, 13},
+ {0x800a, 13},
+ {0x800f, 13},
+ {0x8018, 13},
+ {0x801f, 13},
+ {0x8029, 13},
+ {0xc038, 13},
+ },
+ /* 255 */
+ {
+ {0x8003, 22},
+ {0x8006, 22},
+ {0x800a, 22},
+ {0x800f, 22},
+ {0x8018, 22},
+ {0x801f, 22},
+ {0x8029, 22},
+ {0xc038, 22},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 256 */
+ {
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+};
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_range.c b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c
new file mode 100644
index 0000000000..0ce71480d7
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c
@@ -0,0 +1,62 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_range.h"
+#include "nghttp3_macro.h"
+
+void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) {
+ r->begin = begin;
+ r->end = end;
+}
+
+nghttp3_range nghttp3_range_intersect(const nghttp3_range *a,
+ const nghttp3_range *b) {
+ nghttp3_range r = {0, 0};
+ uint64_t begin = nghttp3_max(a->begin, b->begin);
+ uint64_t end = nghttp3_min(a->end, b->end);
+ if (begin < end) {
+ nghttp3_range_init(&r, begin, end);
+ }
+ return r;
+}
+
+uint64_t nghttp3_range_len(const nghttp3_range *r) { return r->end - r->begin; }
+
+int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b) {
+ return a->begin == b->begin && a->end == b->end;
+}
+
+void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right,
+ const nghttp3_range *a, const nghttp3_range *b) {
+ /* Assume that b is included in a */
+ left->begin = a->begin;
+ left->end = b->begin;
+ right->begin = b->end;
+ right->end = a->end;
+}
+
+int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b) {
+ return a->end <= b->end;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_range.h b/deps/ngtcp2/nghttp3/lib/nghttp3_range.h
new file mode 100644
index 0000000000..20dab69aa6
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_range.h
@@ -0,0 +1,81 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RANGE_H
+#define NGHTTP3_RANGE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/*
+ * nghttp3_range represents half-closed range [begin, end).
+ */
+typedef struct nghttp3_range {
+ uint64_t begin;
+ uint64_t end;
+} nghttp3_range;
+
+/*
+ * nghttp3_range_init initializes |r| with the range [|begin|, |end|).
+ */
+void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end);
+
+/*
+ * nghttp3_range_intersect returns the intersection of |a| and |b|.
+ * If they do not overlap, it returns empty range.
+ */
+nghttp3_range nghttp3_range_intersect(const nghttp3_range *a,
+ const nghttp3_range *b);
+
+/*
+ * nghttp3_range_len returns the length of |r|.
+ */
+uint64_t nghttp3_range_len(const nghttp3_range *r);
+
+/*
+ * nghttp3_range_eq returns nonzero if |a| equals |b|, such that
+ * a->begin == b->begin, and a->end == b->end hold.
+ */
+int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b);
+
+/*
+ * nghttp3_range_cut returns the left and right range after removing
+ * |b| from |a|. This function assumes that |a| completely includes
+ * |b|. In other words, a->begin <= b->begin and b->end <= a->end
+ * hold.
+ */
+void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right,
+ const nghttp3_range *a, const nghttp3_range *b);
+
+/*
+ * nghttp3_range_not_after returns nonzero if the right edge of |a|
+ * does not go beyond of the right edge of |b|.
+ */
+int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b);
+
+#endif /* NGHTTP3_RANGE_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c
new file mode 100644
index 0000000000..1c31ecebf6
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c
@@ -0,0 +1,109 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_rcbuf.h"
+
+#include <assert.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_str.h"
+
+int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
+ const nghttp3_mem *mem) {
+ uint8_t *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_rcbuf) + size);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ *rcbuf_ptr = (void *)p;
+
+ (*rcbuf_ptr)->mem_user_data = mem->mem_user_data;
+ (*rcbuf_ptr)->free = mem->free;
+ (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf);
+ (*rcbuf_ptr)->len = size;
+ (*rcbuf_ptr)->ref = 1;
+
+ return 0;
+}
+
+int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
+ size_t srclen, const nghttp3_mem *mem) {
+ int rv;
+ uint8_t *p;
+
+ rv = nghttp3_rcbuf_new(rcbuf_ptr, srclen + 1, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ (*rcbuf_ptr)->len = srclen;
+ p = (*rcbuf_ptr)->base;
+
+ if (srclen) {
+ p = nghttp3_cpymem(p, src, srclen);
+ }
+
+ *p = '\0';
+
+ return 0;
+}
+
+/*
+ * 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);
+}
+
+void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) {
+ if (rcbuf->ref == -1) {
+ return;
+ }
+
+ ++rcbuf->ref;
+}
+
+void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf) {
+ if (rcbuf == NULL || rcbuf->ref == -1) {
+ return;
+ }
+
+ assert(rcbuf->ref > 0);
+
+ if (--rcbuf->ref == 0) {
+ nghttp3_rcbuf_del(rcbuf);
+ }
+}
+
+nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf) {
+ nghttp3_vec res = {rcbuf->base, rcbuf->len};
+ return res;
+}
+
+int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf) {
+ return rcbuf->ref == -1;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h
new file mode 100644
index 0000000000..feea804000
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h
@@ -0,0 +1,82 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RCBUF_H
+#define NGHTTP3_RCBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#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;
+ /* The pointer to the underlying buffer */
+ uint8_t *base;
+ /* Size of buffer pointed by |base|. */
+ size_t len;
+ /* Reference count */
+ int32_t ref;
+};
+
+/*
+ * Allocates nghttp3_rcbuf object with |size| as initial buffer size.
+ * When the function succeeds, the reference count becomes 1.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM:
+ * Out of memory.
+ */
+int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
+ const nghttp3_mem *mem);
+
+/*
+ * Like nghttp3_rcbuf_new(), but initializes the buffer with |src| of
+ * length |srclen|. This function allocates additional byte at the
+ * end and puts '\0' into it, so that the resulting buffer could be
+ * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to
+ * |srclen|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM:
+ * Out of memory.
+ */
+int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
+ size_t srclen, const nghttp3_mem *mem);
+
+/*
+ * Frees |rcbuf| itself, regardless of its reference cout.
+ */
+void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf);
+
+#endif /* NGHTTP3_RCBUF_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c
new file mode 100644
index 0000000000..9ea91c81c8
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c
@@ -0,0 +1,159 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_ringbuf.h"
+
+#include <assert.h>
+#include <string.h>
+#ifdef WIN32
+# include <intrin.h>
+#endif
+
+#include "nghttp3_macro.h"
+
+#if defined(_MSC_VER) && defined(_M_ARM64)
+unsigned int __popcnt(unsigned int x) {
+ unsigned int c = 0;
+ for (; x; ++c) {
+ x &= x - 1;
+ }
+ return c;
+}
+#endif
+
+int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size,
+ const nghttp3_mem *mem) {
+ if (nmemb) {
+#ifdef WIN32
+ assert(1 == __popcnt((unsigned int)nmemb));
+#else
+ assert(1 == __builtin_popcount((unsigned int)nmemb));
+#endif
+
+ rb->buf = nghttp3_mem_malloc(mem, nmemb * size);
+ if (rb->buf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+ } else {
+ rb->buf = NULL;
+ }
+
+ rb->mem = mem;
+ rb->nmemb = nmemb;
+ rb->size = size;
+ rb->first = 0;
+ rb->len = 0;
+
+ return 0;
+}
+
+void nghttp3_ringbuf_free(nghttp3_ringbuf *rb) {
+ if (rb == NULL) {
+ return;
+ }
+
+ nghttp3_mem_free(rb->mem, rb->buf);
+}
+
+void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb) {
+ rb->first = (rb->first - 1) & (rb->nmemb - 1);
+ rb->len = nghttp3_min(rb->nmemb, rb->len + 1);
+
+ return (void *)&rb->buf[rb->first * rb->size];
+}
+
+void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb) {
+ size_t offset = (rb->first + rb->len) & (rb->nmemb - 1);
+
+ if (rb->len == rb->nmemb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ } else {
+ ++rb->len;
+ }
+
+ return (void *)&rb->buf[offset * rb->size];
+}
+
+void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ --rb->len;
+}
+
+void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb) {
+ assert(rb->len);
+ --rb->len;
+}
+
+void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len) {
+ assert(len <= rb->nmemb);
+ rb->len = len;
+}
+
+void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset) {
+ assert(offset < rb->len);
+ offset = (rb->first + offset) & (rb->nmemb - 1);
+ return &rb->buf[offset * rb->size];
+}
+
+int nghttp3_ringbuf_full(nghttp3_ringbuf *rb) { return rb->len == rb->nmemb; }
+
+int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb) {
+ uint8_t *buf;
+
+ if (rb->nmemb >= nmemb) {
+ return 0;
+ }
+
+#ifdef WIN32
+ assert(1 == __popcnt((unsigned int)nmemb));
+#else
+ assert(1 == __builtin_popcount((unsigned int)nmemb));
+#endif
+
+ buf = nghttp3_mem_malloc(rb->mem, nmemb * rb->size);
+ if (buf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ if (rb->buf != NULL) {
+ if (rb->first + rb->len <= rb->nmemb) {
+ memcpy(buf, rb->buf + rb->first * rb->size, rb->len * rb->size);
+ rb->first = 0;
+ } else {
+ memcpy(buf, rb->buf + rb->first * rb->size,
+ (rb->nmemb - rb->first) * rb->size);
+ memcpy(buf + (rb->nmemb - rb->first) * rb->size, rb->buf,
+ (rb->len - (rb->nmemb - rb->first)) * rb->size);
+ rb->first = 0;
+ }
+
+ nghttp3_mem_free(rb->mem, rb->buf);
+ }
+
+ rb->buf = buf;
+ rb->nmemb = nmemb;
+
+ return 0;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h
new file mode 100644
index 0000000000..8e05ec55b2
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h
@@ -0,0 +1,113 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RINGBUF_H
+#define NGHTTP3_RINGBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+typedef struct nghttp3_ringbuf {
+ /* buf points to the underlying buffer. */
+ uint8_t *buf;
+ const nghttp3_mem *mem;
+ /* nmemb is the number of elements that can be stored in this ring
+ buffer. */
+ size_t nmemb;
+ /* size is the size of each element. */
+ size_t size;
+ /* first is the offset to the first element. */
+ size_t first;
+ /* len is the number of elements actually stored. */
+ size_t len;
+} nghttp3_ringbuf;
+
+/*
+ * nghttp3_ringbuf_init initializes |rb|. |nmemb| is the number of
+ * elements that can be stored in this buffer. |size| is the size of
+ * each element. |size| must be power of 2.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_ringbuf_free frees resources allocated for |rb|. This
+ * function does not free the memory pointed by |rb|.
+ */
+void nghttp3_ringbuf_free(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_push_front moves the offset to the first element in
+ the buffer backward, and returns the pointer to the element.
+ Caller can store data to the buffer pointed by the returned
+ pointer. If this action exceeds the capacity of the ring buffer,
+ the last element is silently overwritten, and rb->len remains
+ unchanged. */
+void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_push_back moves the offset to the last element in
+ the buffer forward, and returns the pointer to the element. Caller
+ can store data to the buffer pointed by the returned pointer. If
+ this action exceeds the capacity of the ring buffer, the first
+ element is silently overwritten, and rb->len remains unchanged. */
+void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb);
+
+/*
+ * nghttp3_ringbuf_pop_front removes first element in |rb|.
+ */
+void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb);
+
+/*
+ * nghttp3_ringbuf_pop_back removes the last element in |rb|.
+ */
+void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_resize changes the number of elements stored. This
+ does not change the capacity of the underlying buffer. */
+void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len);
+
+/* nghttp3_ringbuf_get returns the pointer to the element at
+ |offset|. */
+void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset);
+
+/* nghttp3_ringbuf_len returns the number of elements stored. */
+#define nghttp3_ringbuf_len(RB) ((RB)->len)
+
+/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */
+int nghttp3_ringbuf_full(nghttp3_ringbuf *rb);
+
+int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb);
+
+#endif /* NGHTTP3_RINGBUF_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_str.c b/deps/ngtcp2/nghttp3/lib/nghttp3_str.c
new file mode 100644
index 0000000000..3782aa72cd
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_str.c
@@ -0,0 +1,110 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_str.h"
+
+#include <string.h>
+#include <assert.h>
+
+uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n) {
+ memcpy(dest, src, n);
+ return dest + n;
+}
+
+/* Generated by gendowncasetbl.py */
+static const uint8_t DOWNCASE_TBL[] = {
+ 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */,
+ 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */,
+ 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */,
+ 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */,
+ 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */,
+ 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */,
+ 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */,
+ 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */,
+ 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */,
+ 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */,
+ 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */,
+ 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */,
+ 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */,
+ 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */,
+ 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */,
+ 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */,
+ 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */,
+ 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */,
+ 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */,
+ 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */,
+ 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */,
+ 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */,
+ 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */,
+ 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */,
+ 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */,
+ 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */,
+ 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */,
+ 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */,
+ 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */,
+ 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */,
+ 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */,
+ 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */,
+ 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */,
+ 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */,
+ 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */,
+ 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */,
+ 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */,
+ 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */,
+ 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */,
+ 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */,
+ 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */,
+ 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */,
+ 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */,
+ 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */,
+ 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */,
+ 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */,
+ 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */,
+ 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */,
+ 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */,
+ 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */,
+ 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */,
+ 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */,
+ 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */,
+ 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */,
+ 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */,
+ 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */,
+ 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */,
+ 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */,
+ 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */,
+ 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */,
+ 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */,
+ 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */,
+ 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */,
+ 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */,
+};
+
+void nghttp3_downcase(uint8_t *s, size_t len) {
+ size_t i;
+ for (i = 0; i < len; ++i) {
+ s[i] = DOWNCASE_TBL[s[i]];
+ }
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_str.h b/deps/ngtcp2/nghttp3/lib/nghttp3_str.h
new file mode 100644
index 0000000000..19c1d2c71b
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_str.h
@@ -0,0 +1,40 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_STR_H
+#define NGHTTP3_STR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n);
+
+void nghttp3_downcase(uint8_t *s, size_t len);
+
+#endif /* NGHTTP3_STR_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c
new file mode 100644
index 0000000000..96d60fe82f
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c
@@ -0,0 +1,1287 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_stream.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_conn.h"
+#include "nghttp3_str.h"
+#include "nghttp3_http.h"
+
+/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which
+ makes a copy to outq. */
+#define NGHTTP3_STREAM_MAX_COPY_THRES 128
+
+/* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */
+#define NGHTTP3_MIN_RBLEN 4
+
+int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
+ uint64_t seq, const nghttp3_stream_callbacks *callbacks,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream));
+ nghttp3_node_id nid;
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ 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_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->rx.http.status_code = -1;
+ stream->rx.http.content_length = -1;
+ stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY;
+ stream->error_code = NGHTTP3_H3_NO_ERROR;
+
+ if (callbacks) {
+ stream->callbacks = *callbacks;
+ }
+
+ *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) {
+ nghttp3_typed_buf *tbuf;
+ size_t i, len = nghttp3_ringbuf_len(outq);
+
+ for (i = 0; i < len; ++i) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ if (tbuf->type == NGHTTP3_BUF_TYPE_PRIVATE) {
+ nghttp3_buf_free(&tbuf->buf, mem);
+ }
+ }
+
+ nghttp3_ringbuf_free(outq);
+}
+
+static void delete_chunks(nghttp3_ringbuf *chunks, 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);
+ 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);
+
+ for (i = 0; i < len; ++i) {
+ frent = nghttp3_ringbuf_get(frq, i);
+ switch (frent->fr.hd.type) {
+ 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;
+ }
+ }
+
+ nghttp3_ringbuf_free(frq);
+}
+
+void nghttp3_stream_del(nghttp3_stream *stream) {
+ if (stream == NULL) {
+ return;
+ }
+
+ 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_frq(&stream->frq, stream->mem);
+ nghttp3_tnode_free(&stream->node);
+
+ nghttp3_mem_free(stream->mem, stream);
+}
+
+void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) {
+ memset(rvint, 0, sizeof(*rvint));
+}
+
+void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) {
+ memset(rstate, 0, sizeof(*rstate));
+}
+
+nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint,
+ const uint8_t *src, size_t srclen, int fin) {
+ size_t nread = 0;
+ size_t n;
+ size_t i;
+
+ assert(srclen > 0);
+
+ if (rvint->left == 0) {
+ assert(rvint->acc == 0);
+
+ rvint->left = nghttp3_get_varint_len(src);
+ if (rvint->left <= srclen) {
+ rvint->acc = nghttp3_get_varint(&nread, src);
+ rvint->left = 0;
+ return (nghttp3_ssize)nread;
+ }
+
+ if (fin) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ rvint->acc = nghttp3_get_varint_fb(src);
+ nread = 1;
+ ++src;
+ --srclen;
+ --rvint->left;
+ }
+
+ n = nghttp3_min(rvint->left, srclen);
+
+ for (i = 0; i < n; ++i) {
+ rvint->acc = (rvint->acc << 8) + src[i];
+ }
+
+ rvint->left -= n;
+ nread += n;
+
+ if (fin && rvint->left) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ return (nghttp3_ssize)nread;
+}
+
+int nghttp3_stream_frq_add(nghttp3_stream *stream,
+ const nghttp3_frame_entry *frent) {
+ nghttp3_ringbuf *frq = &stream->frq;
+ nghttp3_frame_entry *dest;
+ int rv;
+
+ if (nghttp3_ringbuf_full(frq)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(frq) * 2);
+ rv = nghttp3_ringbuf_reserve(frq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(frq);
+ *dest = *frent;
+
+ return 0;
+}
+
+int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
+ nghttp3_ringbuf *frq = &stream->frq;
+ nghttp3_frame_entry *frent;
+ int data_eof;
+ int rv;
+
+ for (; nghttp3_ringbuf_len(frq) && !nghttp3_stream_outq_is_full(stream) &&
+ stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) {
+ frent = nghttp3_ringbuf_get(frq, 0);
+
+ switch (frent->fr.hd.type) {
+ case NGHTTP3_FRAME_SETTINGS:
+ rv = nghttp3_stream_write_settings(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_FRAME_HEADERS:
+ rv = nghttp3_stream_write_headers(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ 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) {
+ return rv;
+ }
+ if (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED) {
+ return 0;
+ }
+ if (!data_eof) {
+ return 0;
+ }
+ break;
+ case NGHTTP3_FRAME_MAX_PUSH_ID:
+ if (stream->conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) {
+ break;
+ }
+ rv = nghttp3_stream_write_max_push_id(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ default:
+ /* TODO Not implemented */
+ break;
+ }
+
+ nghttp3_ringbuf_pop_front(frq);
+ }
+
+ return 0;
+}
+
+static void typed_buf_shared_init(nghttp3_typed_buf *tbuf,
+ const nghttp3_buf *chunk) {
+ nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED);
+ tbuf->buf.pos = tbuf->buf.last;
+}
+
+int nghttp3_stream_write_stream_type(nghttp3_stream *stream) {
+ size_t len = nghttp3_put_varint_len((int64_t)stream->type);
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ int rv;
+
+ 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);
+ tbuf.buf.last = chunk->last;
+
+ 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;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ struct {
+ nghttp3_frame_settings settings;
+ nghttp3_settings_entry iv[15];
+ } fr;
+ nghttp3_settings_entry *iv;
+ nghttp3_settings *local_settings = frent->aux.settings.local_settings;
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 3;
+ iv = &fr.settings.iv[0];
+
+ 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[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
+ iv[2].value = local_settings->qpack_blocked_streams;
+
+ len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings);
+
+ 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_frame_write_settings(chunk->last, &fr.settings);
+
+ tbuf.buf.last = chunk->last;
+
+ 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;
+ size_t len;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+
+ len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr);
+
+ 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_frame_write_cancel_push(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;
+ 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);
+
+ 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_frame_write_max_push_id(chunk->last, fr);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_headers(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_headers *fr = &frent->fr.headers;
+ 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_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);
+}
+
+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) {
+ 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;
+
+ nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf));
+
+ rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf,
+ stream->node.nid.id, nva, nvlen);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ pbuflen = nghttp3_buf_len(&pbuf);
+ 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);
+
+ len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen;
+
+ if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) {
+ len += rbuflen;
+ }
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ 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);
+
+ if (rbuflen > NGHTTP3_STREAM_MAX_COPY_THRES) {
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE);
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_init(rbuf);
+ } else if (rbuflen) {
+ chunk->last = nghttp3_cpymem(chunk->last, rbuf->pos, rbuflen);
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_reset(rbuf);
+ }
+
+ if (ebuflen > NGHTTP3_STREAM_MAX_COPY_THRES) {
+ assert(qenc_stream);
+
+ nghttp3_typed_buf_init(&tbuf, ebuf, NGHTTP3_BUF_TYPE_PRIVATE);
+ rv = nghttp3_stream_outq_add(qenc_stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+ nghttp3_buf_init(ebuf);
+ } else if (ebuflen) {
+ assert(qenc_stream);
+
+ rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ chunk = nghttp3_stream_get_chunk(qenc_stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_cpymem(chunk->last, ebuf->pos, ebuflen);
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(qenc_stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_reset(ebuf);
+ }
+
+ assert(0 == nghttp3_buf_len(&pbuf));
+ assert(0 == nghttp3_buf_len(rbuf));
+ assert(0 == nghttp3_buf_len(ebuf));
+
+ return 0;
+
+fail:
+
+ return rv;
+}
+
+int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
+ nghttp3_frame_entry *frent) {
+ int rv;
+ size_t len;
+ nghttp3_typed_buf tbuf;
+ nghttp3_buf buf;
+ nghttp3_buf *chunk;
+ nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data;
+ nghttp3_conn *conn = stream->conn;
+ size_t datalen;
+ uint32_t flags = 0;
+ nghttp3_frame_hd hd;
+ nghttp3_vec vec[8];
+ nghttp3_vec *v;
+ nghttp3_ssize sveccnt;
+ size_t i;
+
+ assert(!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED));
+ assert(read_data);
+ assert(conn);
+
+ *peof = 0;
+
+ sveccnt = read_data(conn, stream->node.nid.id, vec, nghttp3_arraylen(vec),
+ &flags, conn->user_data, stream->user_data);
+ if (sveccnt < 0) {
+ if (sveccnt == NGHTTP3_ERR_WOULDBLOCK) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED;
+ return 0;
+ }
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ datalen = nghttp3_vec_len(vec, (size_t)sveccnt);
+
+ assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF);
+
+ if (flags & NGHTTP3_DATA_FLAG_EOF) {
+ *peof = 1;
+ if (!(flags & NGHTTP3_DATA_FLAG_NO_END_STREAM)) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ if (datalen == 0) {
+ if (nghttp3_stream_outq_write_done(stream)) {
+ /* If this is the last data and its is 0 length, we don't
+ need send DATA frame. We rely on the non-emptiness of
+ outq to schedule stream, so add empty tbuf to outq to
+ just send fin. */
+ nghttp3_buf_init(&buf);
+ nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_PRIVATE);
+ return nghttp3_stream_outq_add(stream, &tbuf);
+ }
+ return 0;
+ }
+ }
+
+ if (datalen == 0) {
+ /* We are going to send more frames, but no DATA frame this
+ time. */
+ return 0;
+ }
+ }
+
+ hd.type = NGHTTP3_FRAME_DATA;
+ hd.length = (int64_t)datalen;
+
+ len = nghttp3_frame_write_hd_len(&hd);
+
+ 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_frame_write_hd(chunk->last, &hd);
+
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (datalen) {
+ for (i = 0; i < (size_t)sveccnt; ++i) {
+ v = &vec[i];
+ if (v->len == 0) {
+ continue;
+ }
+ nghttp3_buf_wrap_init(&buf, v->base, v->len);
+ buf.last = buf.end;
+ nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN);
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) {
+ nghttp3_qpack_decoder *qdec;
+ nghttp3_buf *chunk;
+ int rv;
+ nghttp3_typed_buf tbuf;
+ size_t len;
+
+ assert(stream->conn);
+ assert(stream->conn->tx.qdec == stream);
+
+ qdec = &stream->conn->qdec;
+
+ assert(qdec);
+
+ len = nghttp3_qpack_decoder_get_decoder_streamlen(qdec);
+ if (len == 0) {
+ return 0;
+ }
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ nghttp3_qpack_decoder_write_decoder(qdec, chunk);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_outq_is_full(nghttp3_stream *stream) {
+ /* TODO Verify that the limit is reasonable. */
+ return nghttp3_ringbuf_len(&stream->outq) >= 1024;
+}
+
+int nghttp3_stream_outq_add(nghttp3_stream *stream,
+ const nghttp3_typed_buf *tbuf) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ int rv;
+ nghttp3_typed_buf *dest;
+ size_t len = nghttp3_ringbuf_len(outq);
+
+ stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf);
+
+ if (len) {
+ dest = nghttp3_ringbuf_get(outq, len - 1);
+ if (dest->type == tbuf->type && dest->type == NGHTTP3_BUF_TYPE_SHARED &&
+ dest->buf.begin == tbuf->buf.begin && dest->buf.last == tbuf->buf.pos) {
+ /* If we have already written last entry, adjust outq_idx and
+ offset so that this entry is eligible to send. */
+ if (len == stream->outq_idx) {
+ --stream->outq_idx;
+ stream->outq_offset = nghttp3_buf_len(&dest->buf);
+ }
+
+ dest->buf.last = tbuf->buf.last;
+ /* TODO Is this required? */
+ dest->buf.end = tbuf->buf.end;
+
+ return 0;
+ }
+ }
+
+ if (nghttp3_ringbuf_full(outq)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2);
+ rv = nghttp3_ringbuf_reserve(outq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(outq);
+ *dest = *tbuf;
+
+ return 0;
+}
+
+int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ nghttp3_buf *chunk;
+ size_t len = nghttp3_ringbuf_len(chunks);
+ uint8_t *p;
+ int rv;
+ size_t n = NGHTTP3_STREAM_MIN_CHUNK_SIZE;
+
+ if (len) {
+ chunk = nghttp3_ringbuf_get(chunks, len - 1);
+ if (nghttp3_buf_left(chunk) >= need) {
+ return 0;
+ }
+ }
+
+ for (; n < need; n *= 2)
+ ;
+
+ p = nghttp3_mem_malloc(stream->mem, n);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ if (nghttp3_ringbuf_full(chunks)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2);
+ rv = nghttp3_ringbuf_reserve(chunks, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ chunk = nghttp3_ringbuf_push_back(chunks);
+ nghttp3_buf_wrap_init(chunk, p, n);
+
+ return 0;
+}
+
+nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ size_t len = nghttp3_ringbuf_len(chunks);
+
+ assert(len);
+
+ return nghttp3_ringbuf_get(chunks, len - 1);
+}
+
+int nghttp3_stream_is_blocked(nghttp3_stream *stream) {
+ return (stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) ||
+ (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR) ||
+ (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED);
+}
+
+int nghttp3_stream_require_schedule(nghttp3_stream *stream) {
+ return (!nghttp3_stream_outq_write_done(stream) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR)) ||
+ (nghttp3_ringbuf_len(&stream->frq) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED));
+}
+
+nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
+ nghttp3_vec *vec, size_t veccnt) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t len = nghttp3_ringbuf_len(outq);
+ size_t i;
+ size_t offset = stream->outq_offset;
+ size_t buflen;
+ nghttp3_vec *vbegin = vec, *vend = vec + veccnt;
+ nghttp3_typed_buf *tbuf;
+
+ assert(veccnt > 0);
+
+ for (i = stream->outq_idx; i < len; ++i) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+ if (offset >= buflen) {
+ offset -= buflen;
+ continue;
+ }
+
+ vec->base = tbuf->buf.pos + offset;
+ vec->len = buflen - offset;
+ ++vec;
+ ++i;
+ break;
+ }
+
+ for (; i < len && vec != vend; ++i, ++vec) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ vec->base = tbuf->buf.pos;
+ vec->len = nghttp3_buf_len(&tbuf->buf);
+ }
+
+ /* TODO Rework this if we have finished implementing HTTP
+ messaging */
+ *pfin = nghttp3_ringbuf_len(&stream->frq) == 0 && i == len &&
+ (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM);
+
+ return vec - vbegin;
+}
+
+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;
+ size_t buflen;
+ nghttp3_typed_buf *tbuf;
+
+ for (i = stream->outq_idx; i < len; ++i) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+ if (offset >= buflen) {
+ offset -= buflen;
+ continue;
+ }
+
+ break;
+ }
+
+ assert(i < len || offset == 0);
+
+ stream->unsent_bytes -= n;
+ stream->outq_idx = i;
+ stream->outq_offset = offset;
+
+ return 0;
+}
+
+int nghttp3_stream_outq_write_done(nghttp3_stream *stream) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t len = nghttp3_ringbuf_len(outq);
+
+ return len == 0 || stream->outq_idx >= len;
+}
+
+static int stream_pop_outq_entry(nghttp3_stream *stream,
+ nghttp3_typed_buf *tbuf) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ nghttp3_buf *chunk;
+
+ switch (tbuf->type) {
+ case NGHTTP3_BUF_TYPE_PRIVATE:
+ nghttp3_buf_free(&tbuf->buf, stream->mem);
+ break;
+ case NGHTTP3_BUF_TYPE_ALIEN:
+ break;
+ default:
+ assert(nghttp3_ringbuf_len(chunks));
+
+ chunk = nghttp3_ringbuf_get(chunks, 0);
+
+ assert(chunk->begin == tbuf->buf.begin);
+ assert(chunk->end == tbuf->buf.end);
+
+ if (chunk->last == tbuf->buf.last) {
+ nghttp3_buf_free(chunk, stream->mem);
+ nghttp3_ringbuf_pop_front(chunks);
+ }
+ };
+
+ nghttp3_ringbuf_pop_front(&stream->outq);
+
+ return 0;
+}
+
+int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ uint64_t offset = stream->ack_offset + n;
+ size_t buflen;
+ size_t npopped = 0;
+ size_t nack;
+ nghttp3_typed_buf *tbuf;
+ int rv;
+
+ for (; nghttp3_ringbuf_len(outq);) {
+ tbuf = nghttp3_ringbuf_get(outq, 0);
+ 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;
+ if (stream->callbacks.acked_data) {
+ rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+ stream->ack_done += nack;
+ }
+
+ if (offset >= buflen) {
+ rv = stream_pop_outq_entry(stream, tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+
+ offset -= buflen;
+ ++npopped;
+ stream->ack_done = 0;
+
+ if (stream->outq_idx + 1 == npopped) {
+ stream->outq_offset = 0;
+ break;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ assert(stream->outq_idx + 1 >= npopped);
+ if (stream->outq_idx >= npopped) {
+ stream->outq_idx -= npopped;
+ } else {
+ stream->outq_idx = 0;
+ }
+
+ stream->ack_offset = offset;
+
+ return 0;
+}
+
+int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *data,
+ size_t datalen) {
+ nghttp3_ringbuf *inq = &stream->inq;
+ size_t len = nghttp3_ringbuf_len(inq);
+ nghttp3_buf *buf;
+ size_t nwrite;
+ uint8_t *rawbuf;
+ size_t bufleft;
+ int rv;
+
+ if (len) {
+ buf = nghttp3_ringbuf_get(inq, len - 1);
+ bufleft = nghttp3_buf_left(buf);
+ nwrite = nghttp3_min(datalen, bufleft);
+ buf->last = nghttp3_cpymem(buf->last, data, nwrite);
+ data += nwrite;
+ datalen -= nwrite;
+ }
+
+ for (; datalen;) {
+ if (nghttp3_ringbuf_full(inq)) {
+ size_t nlen =
+ nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(inq) * 2);
+ rv = nghttp3_ringbuf_reserve(inq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rawbuf = nghttp3_mem_malloc(stream->mem, 16384);
+ if (rawbuf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ buf = nghttp3_ringbuf_push_back(inq);
+ nghttp3_buf_wrap_init(buf, rawbuf, 16384);
+ bufleft = nghttp3_buf_left(buf);
+ nwrite = nghttp3_min(datalen, bufleft);
+ buf->last = nghttp3_cpymem(buf->last, data, nwrite);
+ data += nwrite;
+ datalen -= nwrite;
+ }
+
+ return 0;
+}
+
+size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream) {
+ nghttp3_ringbuf *inq = &stream->inq;
+ size_t len = nghttp3_ringbuf_len(inq);
+ size_t i, n = 0;
+ nghttp3_buf *buf;
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(inq, i);
+ n += nghttp3_buf_len(buf);
+ }
+
+ return n;
+}
+
+int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
+ nghttp3_stream_http_event event) {
+ int rv;
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_NONE:
+ return NGHTTP3_ERR_H3_INTERNAL_ERROR;
+ case NGHTTP3_HTTP_STATE_REQ_INITIAL:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ /* TODO Better to check status code */
+ 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:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_DATA_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ /* TODO Better to check status code */
+ 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_MSG_END:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END:
+ if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
+ /* TODO Should ignore unexpected frame in this state as per
+ spec. */
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_END:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ case NGHTTP3_HTTP_STATE_RESP_INITIAL:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ if (stream->rx.http.status_code == -1) {
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN;
+ return 0;
+ }
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) &&
+ 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:
+ if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_DATA_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) &&
+ 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_MSG_END:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END:
+ if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ 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);
+ }
+}
+
+int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) {
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ return 0;
+ default:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+}
+
+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) {
+ return (stream_id & 0x3) == 0;
+}
+
+int nghttp3_client_stream_uni(int64_t stream_id) {
+ return (stream_id & 0x3) == 0x2;
+}
+
+int nghttp3_server_stream_uni(int64_t stream_id) {
+ return (stream_id & 0x3) == 0x3;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h
new file mode 100644
index 0000000000..f7e375c85e
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h
@@ -0,0 +1,407 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_STREAM_H
+#define NGHTTP3_STREAM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_map.h"
+#include "nghttp3_tnode.h"
+#include "nghttp3_ringbuf.h"
+#include "nghttp3_buf.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_qpack.h"
+
+#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256
+
+/* NGHTTP3_MIN_UNSENT_BYTES is the minimum unsent bytes which is large
+ enough to fill outgoing single QUIC packet. */
+#define NGHTTP3_MIN_UNSENT_BYTES 4096
+
+/* NGHTTP3_STREAM_MIN_WRITELEN is the minimum length of write to cause
+ the stream to reschedule. */
+#define NGHTTP3_STREAM_MIN_WRITELEN 800
+
+/* nghttp3_stream_type is unidirectional stream type. */
+typedef enum nghttp3_stream_type {
+ NGHTTP3_STREAM_TYPE_CONTROL = 0x00,
+ NGHTTP3_STREAM_TYPE_PUSH = 0x01,
+ NGHTTP3_STREAM_TYPE_QPACK_ENCODER = 0x02,
+ NGHTTP3_STREAM_TYPE_QPACK_DECODER = 0x03,
+ NGHTTP3_STREAM_TYPE_UNKNOWN = UINT64_MAX,
+} 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;
+
+typedef enum nghttp3_req_stream_state {
+ NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE,
+ 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;
+} nghttp3_varint_read_state;
+
+typedef struct nghttp3_stream_read_state {
+ nghttp3_varint_read_state rvint;
+ nghttp3_frame fr;
+ int state;
+ int64_t left;
+} nghttp3_stream_read_state;
+
+/* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_STREAM_FLAG_NONE 0x0000
+/* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional
+ stream type is identified. */
+#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001
+/* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by
+ QUIC flow control. */
+#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002
+/* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is
+ temporarily unable to provide data. */
+#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004
+/* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application
+ finished to feed outgoing data. */
+#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008
+/* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is
+ blocked due to QPACK decoding. */
+#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010
+/* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent
+ fin. */
+#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020
+/* 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
+/* 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
+
+typedef enum nghttp3_stream_http_state {
+ NGHTTP3_HTTP_STATE_NONE,
+ NGHTTP3_HTTP_STATE_REQ_INITIAL,
+ NGHTTP3_HTTP_STATE_REQ_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_HEADERS_END,
+ NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_DATA_END,
+ NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_TRAILERS_END,
+ NGHTTP3_HTTP_STATE_REQ_END,
+ NGHTTP3_HTTP_STATE_RESP_INITIAL,
+ NGHTTP3_HTTP_STATE_RESP_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_HEADERS_END,
+ NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_DATA_END,
+ NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_TRAILERS_END,
+ NGHTTP3_HTTP_STATE_RESP_END,
+} nghttp3_stream_http_state;
+
+typedef enum nghttp3_stream_http_event {
+ NGHTTP3_HTTP_EVENT_DATA_BEGIN,
+ 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
+ * application is acknowledged by remote endpoint. The number of
+ * bytes acknowledged is given in |datalen|.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning NGHTTP3_ERR_CALLBACK_FAILURE will return to the caller
+ * immediately. Any values other than 0 is treated as
+ * NGHTTP3_ERR_CALLBACK_FAILURE.
+ */
+typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream,
+ int64_t stream_id, size_t datalen,
+ void *user_data);
+
+typedef struct nghttp3_stream_callbacks {
+ nghttp3_stream_acked_data acked_data;
+} nghttp3_stream_callbacks;
+
+typedef struct nghttp3_http_state {
+ /* status_code is HTTP status code received. This field is used
+ if connection is initialized as client. */
+ int32_t status_code;
+ /* content_length is the value of received content-length header
+ field. */
+ int64_t content_length;
+ /* recv_content_length is the number of body bytes received so
+ far. */
+ int64_t recv_content_length;
+ uint16_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;
+};
+
+typedef struct nghttp3_frame_entry {
+ nghttp3_frame fr;
+ union {
+ struct {
+ nghttp3_settings *local_settings;
+ } settings;
+ struct {
+ nghttp3_data_reader dr;
+ } data;
+ } aux;
+} nghttp3_frame_entry;
+
+int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
+ uint64_t seq, const nghttp3_stream_callbacks *callbacks,
+ const nghttp3_mem *mem);
+
+void nghttp3_stream_del(nghttp3_stream *stream);
+
+void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint);
+
+void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate);
+
+nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint,
+ const uint8_t *src, size_t srclen, int fin);
+
+int nghttp3_stream_frq_add(nghttp3_stream *stream,
+ const nghttp3_frame_entry *frent);
+
+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);
+
+int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream);
+
+int nghttp3_stream_outq_is_full(nghttp3_stream *stream);
+
+int nghttp3_stream_outq_add(nghttp3_stream *stream,
+ const nghttp3_typed_buf *tbuf);
+
+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);
+
+int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
+ nghttp3_frame_entry *frent);
+
+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_max_push_id(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need);
+
+nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream);
+
+int nghttp3_stream_is_blocked(nghttp3_stream *stream);
+
+int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n);
+
+/*
+ * nghttp3_stream_outq_write_done returns nonzero if all contents in
+ * outq have been written.
+ */
+int nghttp3_stream_outq_write_done(nghttp3_stream *stream);
+
+int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n);
+
+/*
+ * nghttp3_stream_is_active returns nonzero if |stream| is active. In
+ * other words, it has something to send. This function does not take
+ * into account its descendants.
+ */
+int nghttp3_stream_is_active(nghttp3_stream *stream);
+
+/*
+ * nghttp3_stream_require_schedule returns nonzero if |stream| should
+ * be scheduled. In other words, |stream| or its descendants have
+ * something to send.
+ */
+int nghttp3_stream_require_schedule(nghttp3_stream *stream);
+
+int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *src,
+ size_t srclen);
+
+size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream);
+
+int nghttp3_stream_ensure_qpack_stream_context(nghttp3_stream *stream);
+
+void nghttp3_stream_delete_qpack_stream_context(nghttp3_stream *stream);
+
+int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
+ nghttp3_stream_http_event event);
+
+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.
+ */
+int nghttp3_stream_uni(int64_t stream_id);
+
+/*
+ * nghttp3_client_stream_bidi returns nonzero if stream identified by
+ * |stream_id| is client initiated bidirectional stream.
+ */
+int nghttp3_client_stream_bidi(int64_t stream_id);
+
+/*
+ * nghttp3_client_stream_uni returns nonzero if stream identified by
+ * |stream_id| is client initiated unidirectional stream.
+ */
+int nghttp3_client_stream_uni(int64_t stream_id);
+
+/*
+ * nghttp3_server_stream_uni returns nonzero if stream identified by
+ * |stream_id| is server initiated unidirectional stream.
+ */
+int nghttp3_server_stream_uni(int64_t stream_id);
+
+#endif /* NGHTTP3_STREAM_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c
new file mode 100644
index 0000000000..94dca7dbf7
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c
@@ -0,0 +1,110 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_tnode.h"
+
+#include <assert.h>
+
+#include "nghttp3_macro.h"
+#include "nghttp3_stream.h"
+#include "nghttp3_conn.h"
+#include "nghttp3_conv.h"
+
+nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid,
+ nghttp3_node_id_type type, int64_t id) {
+ nid->type = type;
+ nid->id = id;
+ return nid;
+}
+
+int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b) {
+ return a->type == b->type && a->id == b->id;
+}
+
+void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid,
+ uint64_t seq, uint8_t pri) {
+ assert(nghttp3_pri_uint8_urgency(pri) < NGHTTP3_URGENCY_LEVELS);
+
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+ tnode->nid = *nid;
+ tnode->seq = seq;
+ tnode->cycle = 0;
+ tnode->pri = pri;
+}
+
+void nghttp3_tnode_free(nghttp3_tnode *tnode) { (void)tnode; }
+
+static void tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) {
+ assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(pq, &tnode->pe);
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+}
+
+void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) {
+ if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ return;
+ }
+
+ tnode_unschedule(tnode, pq);
+}
+
+static uint64_t pq_get_first_cycle(nghttp3_pq *pq) {
+ nghttp3_tnode *top;
+
+ if (nghttp3_pq_empty(pq)) {
+ return 0;
+ }
+
+ top = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe);
+ return top->cycle;
+}
+
+int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
+ size_t nwrite) {
+ uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN;
+
+ if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ tnode->cycle = pq_get_first_cycle(pq) +
+ ((nwrite == 0 || !nghttp3_pri_uint8_inc(tnode->pri))
+ ? 0
+ : nghttp3_max(1, penalty));
+ } else if (nwrite > 0) {
+ if (!nghttp3_pri_uint8_inc(tnode->pri) || nghttp3_pq_size(pq) == 1) {
+ return 0;
+ }
+
+ nghttp3_pq_remove(pq, &tnode->pe);
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+ tnode->cycle += nghttp3_max(1, penalty);
+ } else {
+ return 0;
+ }
+
+ return nghttp3_pq_push(pq, &tnode->pe);
+}
+
+int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) {
+ return 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
new file mode 100644
index 0000000000..817aec034c
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h
@@ -0,0 +1,82 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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_TNODE_H
+#define NGHTTP3_TNODE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_pq.h"
+
+#define NGHTTP3_TNODE_MAX_CYCLE_GAP (1llu << 24)
+
+typedef enum nghttp3_node_id_type {
+ NGHTTP3_NODE_ID_TYPE_STREAM = 0x00,
+ NGHTTP3_NODE_ID_TYPE_PUSH = 0x01,
+} nghttp3_node_id_type;
+
+typedef struct nghttp3_node_id {
+ nghttp3_node_id_type type;
+ int64_t id;
+} nghttp3_node_id;
+
+nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid,
+ nghttp3_node_id_type type, int64_t id);
+
+int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b);
+
+typedef struct nghttp3_tnode {
+ nghttp3_pq_entry pe;
+ size_t num_children;
+ nghttp3_node_id nid;
+ uint64_t seq;
+ uint64_t cycle;
+ /* pri is a stream priority produced by nghttp3_pri_to_uint8. */
+ uint8_t pri;
+} nghttp3_tnode;
+
+void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid,
+ uint64_t seq, uint8_t pri);
+
+void nghttp3_tnode_free(nghttp3_tnode *tnode);
+
+void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq);
+
+/*
+ * nghttp3_tnode_schedule schedules |tnode| using |nwrite| as penalty.
+ * 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);
+
+/*
+ * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled.
+ */
+int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode);
+
+#endif /* NGHTTP3_TNODE_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c
new file mode 100644
index 0000000000..ab292ac8ae
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c
@@ -0,0 +1,38 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_vec.h"
+#include "nghttp3_macro.h"
+
+size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
+ size_t i;
+ size_t res = 0;
+
+ for (i = 0; i < n; ++i) {
+ res += vec[i].len;
+ }
+
+ return res;
+}
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h
new file mode 100644
index 0000000000..c1a928e3e1
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h
@@ -0,0 +1,35 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_VEC_H
+#define NGHTTP3_VEC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#endif /* NGHTTP3_VEC_H */
diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c
new file mode 100644
index 0000000000..dfad4793c4
--- /dev/null
+++ b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c
@@ -0,0 +1,39 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 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 <nghttp3/nghttp3.h>
+
+static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM,
+ NGHTTP3_VERSION};
+
+nghttp3_info *nghttp3_version(int least_version) {
+ if (least_version > NGHTTP3_VERSION_NUM) {
+ return NULL;
+ }
+ return &version;
+}
diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp
new file mode 100644
index 0000000000..9ea93be209
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2.gyp
@@ -0,0 +1,174 @@
+{
+ 'target_defaults': {
+ 'defines': [ '_U_=' ]
+ },
+ 'variables': {
+ 'ngtcp2_sources': [
+ 'ngtcp2/lib/ngtcp2_acktr.c',
+ 'ngtcp2/lib/ngtcp2_addr.c',
+ 'ngtcp2/lib/ngtcp2_buf.c',
+ 'ngtcp2/lib/ngtcp2_cc.c',
+ 'ngtcp2/lib/ngtcp2_cid.c',
+ 'ngtcp2/lib/ngtcp2_conn.c',
+ 'ngtcp2/lib/ngtcp2_conv.c',
+ 'ngtcp2/lib/ngtcp2_crypto.c',
+ 'ngtcp2/lib/ngtcp2_err.c',
+ 'ngtcp2/lib/ngtcp2_gaptr.c',
+ 'ngtcp2/lib/ngtcp2_idtr.c',
+ 'ngtcp2/lib/ngtcp2_ksl.c',
+ 'ngtcp2/lib/ngtcp2_log.c',
+ 'ngtcp2/lib/ngtcp2_map.c',
+ 'ngtcp2/lib/ngtcp2_mem.c',
+ 'ngtcp2/lib/ngtcp2_path.c',
+ 'ngtcp2/lib/ngtcp2_pkt.c',
+ 'ngtcp2/lib/ngtcp2_ppe.c',
+ 'ngtcp2/lib/ngtcp2_pq.c',
+ 'ngtcp2/lib/ngtcp2_pv.c',
+ 'ngtcp2/lib/ngtcp2_qlog.c',
+ 'ngtcp2/lib/ngtcp2_range.c',
+ 'ngtcp2/lib/ngtcp2_ringbuf.c',
+ 'ngtcp2/lib/ngtcp2_rob.c',
+ 'ngtcp2/lib/ngtcp2_rst.c',
+ 'ngtcp2/lib/ngtcp2_rtb.c',
+ 'ngtcp2/lib/ngtcp2_str.c',
+ 'ngtcp2/lib/ngtcp2_strm.c',
+ 'ngtcp2/lib/ngtcp2_vec.c',
+ 'ngtcp2/lib/ngtcp2_version.c',
+ 'ngtcp2/crypto/shared.c',
+ ],
+ 'ngtcp2_sources_openssl': [
+ 'ngtcp2/crypto/openssl/openssl.c'
+ ],
+ 'ngtcp2_sources_boringssl': [
+ 'ngtcp2/crypto/boringssl/boringssl.c'
+ ],
+ 'nghttp3_sources': [
+ 'nghttp3/lib/nghttp3_buf.c',
+ 'nghttp3/lib/nghttp3_conn.c',
+ 'nghttp3/lib/nghttp3_conv.c',
+ 'nghttp3/lib/nghttp3_debug.c',
+ 'nghttp3/lib/nghttp3_err.c',
+ 'nghttp3/lib/nghttp3_frame.c',
+ 'nghttp3/lib/nghttp3_gaptr.c',
+ 'nghttp3/lib/nghttp3_http.c',
+ 'nghttp3/lib/nghttp3_idtr.c',
+ 'nghttp3/lib/nghttp3_ksl.c',
+ 'nghttp3/lib/nghttp3_map.c',
+ 'nghttp3/lib/nghttp3_mem.c',
+ 'nghttp3/lib/nghttp3_pq.c',
+ 'nghttp3/lib/nghttp3_qpack.c',
+ 'nghttp3/lib/nghttp3_qpack_huffman.c',
+ 'nghttp3/lib/nghttp3_qpack_huffman_data.c',
+ 'nghttp3/lib/nghttp3_range.c',
+ 'nghttp3/lib/nghttp3_rcbuf.c',
+ 'nghttp3/lib/nghttp3_ringbuf.c',
+ 'nghttp3/lib/nghttp3_str.c',
+ 'nghttp3/lib/nghttp3_stream.c',
+ 'nghttp3/lib/nghttp3_tnode.c',
+ 'nghttp3/lib/nghttp3_vec.c',
+ 'nghttp3/lib/nghttp3_version.c'
+ ]
+ },
+ 'targets': [
+ {
+ 'target_name': 'ngtcp2',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '',
+ 'ngtcp2/lib/includes/',
+ 'ngtcp2/crypto/includes/',
+ 'ngtcp2/lib/',
+ 'ngtcp2/crypto/',
+ ],
+ 'defines': [
+ 'BUILDING_NGTCP2',
+ 'NGTCP2_STATICLIB',
+ ],
+ 'conditions': [
+ ['node_shared_openssl=="false"', {
+ 'dependencies': [
+ '../openssl/openssl.gyp:openssl'
+ ]
+ }],
+ ['OS=="win"', {
+ 'defines': [
+ 'WIN32',
+ '_WINDOWS',
+ 'HAVE_CONFIG_H',
+ ],
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'CompileAs': '1'
+ },
+ },
+ }],
+ ['OS=="linux"', {
+ 'defines': [
+ 'HAVE_ARPA_INET_H',
+ 'HAVE_NETINET_IN_H',
+ ],
+ }],
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'NGTCP2_STATICLIB',
+ ],
+ 'include_dirs': [
+ '',
+ 'ngtcp2/lib/includes',
+ 'ngtcp2/crypto/includes',
+ ]
+ },
+ 'sources': [
+ '<@(ngtcp2_sources)',
+ '<@(ngtcp2_sources_openssl)',
+ ]
+ },
+ {
+ 'target_name': 'nghttp3',
+ 'type': 'static_library',
+ 'include_dirs': [
+ 'nghttp3/lib/includes/',
+ 'nghttp3/lib/'
+ ],
+ 'defines': [
+ 'BUILDING_NGHTTP3',
+ 'NGHTTP3_STATICLIB'
+ ],
+ 'dependencies': [
+ 'ngtcp2'
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'defines': [
+ 'WIN32',
+ '_WINDOWS',
+ 'HAVE_CONFIG_H',
+ ],
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'CompileAs': '1'
+ },
+ },
+ }],
+ ['OS=="linux"', {
+ 'defines': [
+ 'HAVE_ARPA_INET_H',
+ 'HAVE_NETINET_IN_H',
+ ],
+ }],
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'NGHTTP3_STATICLIB'
+ ],
+ 'include_dirs': [
+ 'nghttp3/lib/includes'
+ ]
+ },
+ 'sources': [
+ '<@(nghttp3_sources)'
+ ]
+ }
+ ]
+}
diff --git a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c
new file mode 100644
index 0000000000..8f75b485e3
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c
@@ -0,0 +1,540 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#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_boringssl.h>
+
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/chacha.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_CHACHA20,
+} ngtcp2_crypto_boringssl_cipher_type;
+
+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_evp_aes_256_ctr = {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR,
+};
+
+static ngtcp2_crypto_boringssl_cipher crypto_cipher_chacha20 = {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20,
+};
+
+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->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 = EVP_AEAD_max_overhead(aead->native_handle);
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm());
+}
+
+static const EVP_AEAD *crypto_ssl_get_aead(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ return EVP_aead_aes_128_gcm();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return EVP_aead_aes_256_gcm();
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_aead_chacha20_poly1305();
+ default:
+ return NULL;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_encryption(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ default:
+ return 0;
+ }
+}
+
+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;
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return &crypto_cipher_evp_aes_256_ctr;
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return &crypto_cipher_chacha20;
+ default:
+ return NULL;
+ }
+}
+
+static const EVP_MD *crypto_ssl_get_md(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_sha256();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return EVP_sha384();
+ default:
+ return NULL;
+ }
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ SSL *ssl = tls_native_handle;
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_ssl_get_aead(ssl));
+ ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl);
+ ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl);
+ ctx->max_encryption = crypto_ssl_get_aead_max_encryption(ssl);
+ ctx->max_decryption_failure = crypto_ssl_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 EVP_MD *md) {
+ return (size_t)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 EVP_AEAD *aead) {
+ return (size_t)EVP_AEAD_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 EVP_AEAD *aead) {
+ return (size_t)EVP_AEAD_nonce_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 EVP_AEAD *cipher = aead->native_handle;
+ size_t keylen = crypto_aead_keylen(cipher);
+ EVP_AEAD_CTX *actx;
+
+ (void)noncelen;
+
+ actx = EVP_AEAD_CTX_new(cipher, key, keylen, EVP_AEAD_DEFAULT_TAG_LENGTH);
+ 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) {
+ return ngtcp2_crypto_aead_ctx_encrypt_init(aead_ctx, aead, key, noncelen);
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ EVP_AEAD_CTX_free(aead_ctx->native_handle);
+ }
+}
+
+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;
+ union {
+ /* ctx is EVP_CIPHER_CTX used when type ==
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP. */
+ EVP_CIPHER_CTX *ctx;
+ /* key contains an encryption key when type ==
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20. */
+ uint8_t key[32];
+ };
+} ngtcp2_crypto_boringssl_cipher_ctx;
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ 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;
+ }
+
+ 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;
+
+ cipher_ctx->native_handle = ctx;
+
+ return 0;
+}
+
+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);
+}
+
+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) {
+ const EVP_MD *prf = md->native_handle;
+ size_t destlen = (size_t)EVP_MD_size(prf);
+
+ if (HKDF_extract(dest, &destlen, prf, secret, secretlen, salt, saltlen) !=
+ 1) {
+ 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) {
+ const EVP_MD *prf = md->native_handle;
+
+ if (HKDF_expand(dest, destlen, prf, secret, secretlen, 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 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) {
+ 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 *ad, size_t adlen) {
+ EVP_AEAD_CTX *actx = aead_ctx->native_handle;
+ size_t max_outlen = ciphertextlen;
+ size_t outlen;
+
+ (void)aead;
+
+ if (EVP_AEAD_CTX_open(actx, dest, &outlen, max_outlen, nonce, noncelen,
+ ciphertext, ciphertextlen, ad, adlen) != 1) {
+ 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";
+ 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;
+ }
+ return 0;
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_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);
+#else /* !WORDS_BIGENDIAN */
+ memcpy(&counter, sample, sizeof(counter));
+#endif /* !WORDS_BIGENDIAN */
+ CRYPTO_chacha_20(dest, PLAINTEXT, sizeof(PLAINTEXT) - 1, ctx->key,
+ sample + sizeof(counter), counter);
+ return 0;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn);
+ int rv;
+ int err;
+
+ if (SSL_provide_quic_data(
+ ssl, ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(crypto_level),
+ data, datalen) != 1) {
+ return -1;
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ retry:
+ rv = SSL_do_handshake(ssl);
+ if (rv <= 0) {
+ err = SSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ return -1;
+ case SSL_ERROR_EARLY_DATA_REJECTED:
+ assert(!ngtcp2_conn_is_server(conn));
+
+ SSL_reset_early_data_reject(ssl);
+
+ rv = ngtcp2_conn_early_data_rejected(conn);
+ if (rv != 0) {
+ return -1;
+ }
+
+ goto retry;
+ default:
+ return -1;
+ }
+ }
+
+ if (SSL_in_early_data(ssl)) {
+ return 0;
+ }
+
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = SSL_process_quic_post_handshake(ssl);
+ if (rv != 1) {
+ err = SSL_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) {
+ 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);
+ if (rv != 0) {
+ 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) {
+ if (SSL_set_quic_transport_params(tls, buf, len) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_boringssl_from_ssl_encryption_level(
+ enum ssl_encryption_level_t ssl_level) {
+ switch (ssl_level) {
+ case ssl_encryption_initial:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case ssl_encryption_early_data:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case ssl_encryption_handshake:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case ssl_encryption_application:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ }
+}
+
+enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return ssl_encryption_initial;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return ssl_encryption_handshake;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return ssl_encryption_application;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return ssl_encryption_early_data;
+ default:
+ assert(0);
+ }
+}
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
new file mode 100644
index 0000000000..23901d18c1
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
@@ -0,0 +1,688 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_H
+#define NGTCP2_CRYPTO_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_SECRETLEN` is the length of secret
+ * for Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_KEYLEN` is the length of key for
+ * Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_KEYLEN 16
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_IVLEN` is the length of IV for
+ * Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_IVLEN 12
+
+/**
+ * @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.
+ *
+ * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
+ * a pointer to SSL object.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_tls_early` initializes |ctx| by extracting early
+ * ciphers and message digests from native TLS session
+ * |tls_native_handle|. This is used for encrypting/decrypting 0RTT
+ * packets.
+ *
+ * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
+ * a pointer to SSL object.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_ctx *
+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.
+ *
+ * If libngtcp2_crypto_openssl is linked, |md_native_handle| must be a
+ * pointer to EVP_MD.
+ *
+ * If libngtcp2_crypto_gnutls is linked, |md_native_handle| must be
+ * gnutls_mac_algorithm_t casted to ``void *``.
+ *
+ * If libngtcp2_crypto_boringssl is linked, |md_native_handle| must be
+ * a pointer to EVP_MD.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
+ void *md_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_md_hashlen` returns the length of |md| output.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_keylen` returns the length of key for |aead|.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_noncelen` returns the length of nonce for
+ * |aead|.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_extract` performs HKDF extract operation. The
+ * result is the length of |md| and is stored to the buffer pointed by
+ * |dest|. The caller is responsible to specify the buffer that can
+ * store the output.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN 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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_expand` performs HKDF expand 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_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);
+
+/**
+ * @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|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen,
+ const uint8_t *label,
+ size_t labellen);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_crypto_side` indicates which side the application
+ * implements; client or server.
+ */
+typedef enum ngtcp2_crypto_side {
+ /**
+ * :enum:`NGTCP2_CRYPTO_SIDE_CLIENT` indicates that the application
+ * is client.
+ */
+ NGTCP2_CRYPTO_SIDE_CLIENT,
+ /**
+ * :enum:`NGTCP2_CRYPTO_SIDE_SERVER` indicates that the application
+ * is server.
+ */
+ NGTCP2_CRYPTO_SIDE_SERVER
+} ngtcp2_crypto_side;
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_packet_protection_ivlen` returns the length of IV
+ * used to encrypt QUIC packet.
+ */
+NGTCP2_EXTERN size_t
+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|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN 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);
+
+/**
+ * @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.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN 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);
+
+/**
+ * @function
+ *
+ * `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|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN 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);
+
+/**
+ * @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.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_TLS_DECRYPT`.
+ */
+NGTCP2_EXTERN 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);
+
+/**
+ * @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.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest,
+ const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample);
+
+/**
+ * @function
+ *
+ * `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.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_rx_key` derives the rx keys from
+ * |secret| and installs new keys to |conn|.
+ *
+ * If |key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |key|. If |iv| is
+ * not NULL, the derived packet protection IV for decryption is
+ * written to the buffer pointed by |iv|. If |hp| is not NULL, the
+ * derived header protection key for decryption is written to the
+ * buffer pointed by |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`.
+ *
+ * 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.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key(
+ ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp,
+ ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_tx_key` derives the tx keys from
+ * |secret| and installs new keys to |conn|.
+ *
+ * If |key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |key|. If |iv| is
+ * not NULL, the derived packet protection IV for encryption is
+ * written to the buffer pointed by |iv|. If |hp| is not NULL, the
+ * derived header protection key for encryption is written to the
+ * buffer pointed by |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`.
+ *
+ * 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|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key(
+ ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp,
+ ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_update_key` updates traffic keying materials.
+ *
+ * The new traffic secret for decryption is written to the buffer
+ * pointed by |rx_secret|. The length of secret is |secretlen| bytes,
+ * and |rx_secret| must point to the buffer which has enough capacity.
+ *
+ * The new traffic secret for encryption is written to the buffer
+ * pointed by |tx_secret|. The length of secret is |secretlen| bytes,
+ * and |tx_secret| must point to the buffer which has enough capacity.
+ *
+ * The derived packet protection key for decryption is written to the
+ * buffer pointed by |rx_key|. The derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|.
+ * |rx_aead_ctx| must be constructed with |rx_key|.
+ *
+ * The derived packet protection key for encryption is written to the
+ * buffer pointed by |tx_key|. The derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|.
+ * |tx_aead_ctx| must be constructed with |rx_key|.
+ *
+ * |current_rx_secret| and |current_tx_secret| are the current traffic
+ * secrets for decryption and encryption. |secretlen| specifies the
+ * 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`.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN 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,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `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.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_client_initial_cb` installs initial secrets and
+ * 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.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_retry_cb` re-installs initial secrets in
+ * response to incoming Retry packet.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_conn_callbacks.recv_retry` field. It is only used
+ * by client.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_client_initial_cb` installs initial secrets in
+ * response to an incoming Initial packet from client, and sets QUIC
+ * transport parameters.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_conn_callbacks.recv_client_initial` field. It is
+ * only used by server.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_read_write_crypto_data` reads CRYPTO data |data| of
+ * 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.
+ *
+ * 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
+ * suitable. The error codes less than -10000 are specific to
+ * underlying TLS implementation. For OpenSSL, the error codes are
+ * defined in *ngtcp2_crypto_openssl.h*.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen);
+
+/**
+ * @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.
+ *
+ * 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);
+
+/**
+ * @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.
+ *
+ * This function wraps around `ngtcp2_pkt_write_connection_close` for
+ * easier use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer
+ * pointed by |dest| of length |destlen|. |odcid| specifies Original
+ * Destination Connection ID. |token| specifies Retry Token, and
+ * |tokenlen| specifies its length.
+ *
+ * This function wraps around `ngtcp2_pkt_write_retry` for easier use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_retry(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token,
+ size_t tokenlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_encrypt_init` initializes |aead_ctx| with
+ * new AEAD cipher context object for encryption which is constructed
+ * to use |key| as encryption key. |aead| specifies AEAD cipher to
+ * use. |noncelen| is the length of nonce.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN 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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_decrypt_init` initializes |aead_ctx| with
+ * new AEAD cipher context object for decryption which is constructed
+ * to use |key| as encryption key. |aead| specifies AEAD cipher to
+ * use. |noncelen| is the length of nonce.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN 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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_free` frees up resources used by
+ * |aead_ctx|. This function does not free the memory pointed by
+ * |aead_ctx| itself.
+ */
+NGTCP2_EXTERN void
+ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `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.
+ */
+NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_delete_crypto_cipher_ctx_cb` deletes the given
+ * |cipher_ctx|.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_conn_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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_H */
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h
new file mode 100644
index 0000000000..65f8414249
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h
@@ -0,0 +1,62 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_BORINGSSL_H
+#define NGTCP2_CRYPTO_BORINGSSL_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <openssl/ssl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_from_ssl_encryption_level` translates
+ * |ssl_level| to :type:`ngtcp2_crypto_level`. This function is only
+ * available for BoringSSL backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_boringssl_from_ssl_encryption_level(
+ enum ssl_encryption_level_t ssl_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to ssl_encryption_level_t. This function is only
+ * available for BoringSSL backend.
+ */
+NGTCP2_EXTERN enum ssl_encryption_level_t
+ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_BORINGSSL_H */
diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h
new file mode 100644
index 0000000000..10a8e712cf
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h
@@ -0,0 +1,90 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_OPENSSL_H
+#define NGTCP2_CRYPTO_OPENSSL_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <openssl/ssl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @macrosection
+ *
+ * OpenSSL specific error codes
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP` is the
+ * error code which indicates that TLS handshake routine is
+ * interrupted by X509 certificate lookup. See
+ * :macro:`SSL_ERROR_WANT_X509_LOOKUP` error description from
+ * `SSL_do_handshake`.
+ */
+#define NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP -10001
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB` is the
+ * error code which indicates that TLS handshake routine is
+ * interrupted by client hello callback. See
+ * :macro:`SSL_ERROR_WANT_CLIENT_HELLO_CB` error description from
+ * `SSL_do_handshake`.
+ */
+#define NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB -10002
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_from_ossl_encryption_level` translates
+ * |ossl_level| to :type:`ngtcp2_crypto_level`. This function is only
+ * available for OpenSSL backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_openssl_from_ossl_encryption_level(
+ OSSL_ENCRYPTION_LEVEL ossl_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to OSSL_ENCRYPTION_LEVEL. This function is only
+ * available for OpenSSL backend.
+ */
+NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_OPENSSL_H */
diff --git a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c
new file mode 100644
index 0000000000..d4b9e57f27
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c
@@ -0,0 +1,526 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+
+#include "shared.h"
+
+static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) {
+ switch (EVP_CIPHER_nid(aead)) {
+ case NID_aes_128_gcm:
+ case NID_aes_256_gcm:
+ return EVP_GCM_TLS_TAG_LEN;
+ case NID_chacha20_poly1305:
+ return EVP_CHACHAPOLY_TLS_TAG_LEN;
+ case NID_aes_128_ccm:
+ return EVP_CCM_TLS_TAG_LEN;
+ default:
+ assert(0);
+ }
+}
+
+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();
+ ctx->hp.native_handle = (void *)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 = crypto_aead_max_overhead(aead_native_handle);
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm());
+}
+
+static const EVP_CIPHER *crypto_ssl_get_aead(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ return EVP_aes_128_gcm();
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return EVP_aes_256_gcm();
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_chacha20_poly1305();
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return EVP_aes_128_ccm();
+ default:
+ return NULL;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_encryption(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+static const EVP_CIPHER *crypto_ssl_get_hp(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return EVP_aes_128_ctr();
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return EVP_aes_256_ctr();
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_chacha20();
+ default:
+ return NULL;
+ }
+}
+
+static const EVP_MD *crypto_ssl_get_md(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return EVP_sha256();
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return EVP_sha384();
+ default:
+ return NULL;
+ }
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ SSL *ssl = tls_native_handle;
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_ssl_get_aead(ssl));
+ ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl);
+ ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl);
+ ctx->max_encryption = crypto_ssl_get_aead_max_encryption(ssl);
+ ctx->max_decryption_failure = crypto_ssl_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 EVP_MD *md) {
+ return (size_t)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 EVP_CIPHER *aead) {
+ return (size_t)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 EVP_CIPHER *aead) {
+ return (size_t)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 EVP_CIPHER *cipher = aead->native_handle;
+ int cipher_nid = EVP_CIPHER_nid(cipher);
+ EVP_CIPHER_CTX *actx;
+
+ actx = EVP_CIPHER_CTX_new();
+ if (actx == NULL) {
+ return -1;
+ }
+
+ if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
+ !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_EncryptInit_ex(actx, NULL, NULL, key, NULL)) {
+ EVP_CIPHER_CTX_free(actx);
+ 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 EVP_CIPHER *cipher = aead->native_handle;
+ int cipher_nid = EVP_CIPHER_nid(cipher);
+ EVP_CIPHER_CTX *actx;
+
+ actx = EVP_CIPHER_CTX_new();
+ if (actx == NULL) {
+ return -1;
+ }
+
+ if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
+ !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_DecryptInit_ex(actx, NULL, NULL, key, NULL)) {
+ EVP_CIPHER_CTX_free(actx);
+ 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) {
+ 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) {
+ EVP_CIPHER_CTX *actx;
+
+ actx = EVP_CIPHER_CTX_new();
+ if (actx == NULL) {
+ return -1;
+ }
+
+ if (!EVP_EncryptInit_ex(actx, cipher->native_handle, NULL, key, NULL)) {
+ EVP_CIPHER_CTX_free(actx);
+ 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) {
+ 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) {
+ const EVP_MD *prf = md->native_handle;
+ int rv = 0;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ size_t destlen = (size_t)EVP_MD_size(prf);
+
+ if (pctx == NULL) {
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1 ||
+ EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 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_derive(pctx, dest, &destlen) != 1) {
+ rv = -1;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+
+ return rv;
+}
+
+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) {
+ 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_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_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;
+}
+
+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 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;
+
+ (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, dest, &len, plaintext, (int)plaintextlen) ||
+ !EVP_EncryptFinal_ex(actx, dest + len, &len) ||
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen,
+ dest + plaintextlen)) {
+ 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 *ad, size_t adlen) {
+ 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;
+
+ (void)noncelen;
+
+ if (taglen > ciphertextlen) {
+ return -1;
+ }
+
+ ciphertextlen -= taglen;
+ tag = ciphertext + ciphertextlen;
+
+ if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) ||
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen,
+ (uint8_t *)tag) ||
+ (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, dest, &len, ciphertext, (int)ciphertextlen) ||
+ (cipher_nid != NID_aes_128_ccm &&
+ !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) {
+ 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";
+ EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
+ int len;
+
+ (void)hp;
+
+ 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;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn);
+ int rv;
+ int err;
+
+ if (SSL_provide_quic_data(
+ ssl, ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(crypto_level),
+ data, datalen) != 1) {
+ return -1;
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ rv = SSL_do_handshake(ssl);
+ if (rv <= 0) {
+ err = SSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+ return NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ return NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP;
+ case SSL_ERROR_SSL:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = SSL_process_quic_post_handshake(ssl);
+ if (rv != 1) {
+ err = SSL_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) {
+ 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);
+ if (rv != 0) {
+ 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) {
+ if (SSL_set_quic_transport_params(tls, buf, len) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_openssl_from_ossl_encryption_level(
+ OSSL_ENCRYPTION_LEVEL ossl_level) {
+ switch (ossl_level) {
+ case ssl_encryption_initial:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case ssl_encryption_early_data:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case ssl_encryption_handshake:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case ssl_encryption_application:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ }
+}
+
+OSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return ssl_encryption_initial;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return ssl_encryption_handshake;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return ssl_encryption_application;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return ssl_encryption_early_data;
+ default:
+ assert(0);
+ }
+}
diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.c b/deps/ngtcp2/ngtcp2/crypto/shared.c
new file mode 100644
index 0000000000..5d040f2ce7
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/shared.c
@@ -0,0 +1,831 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "shared.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
+ void *md_native_handle) {
+ md->native_handle = md_native_handle;
+ return md;
+}
+
+int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *label, size_t labellen) {
+ static const uint8_t LABEL[] = "tls13 ";
+ uint8_t info[256];
+ uint8_t *p = info;
+
+ *p++ = (uint8_t)(destlen / 256);
+ *p++ = (uint8_t)(destlen % 256);
+ *p++ = (uint8_t)(sizeof(LABEL) - 1 + labellen);
+ memcpy(p, LABEL, sizeof(LABEL) - 1);
+ p += sizeof(LABEL) - 1;
+ memcpy(p, label, labellen);
+ p += labellen;
+ *p++ = 0;
+
+ return ngtcp2_crypto_hkdf_expand(dest, destlen, md, secret, secretlen, info,
+ (size_t)(p - info));
+}
+
+#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32
+
+int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
+ uint8_t *tx_secret,
+ uint8_t *initial_secret,
+ const ngtcp2_cid *client_dcid,
+ ngtcp2_crypto_side side) {
+ static const uint8_t CLABEL[] = "client in";
+ static const uint8_t SLABEL[] = "server in";
+ uint8_t initial_secret_buf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t *client_secret;
+ uint8_t *server_secret;
+ ngtcp2_crypto_ctx ctx;
+ const uint8_t *salt;
+ size_t saltlen;
+
+ if (!initial_secret) {
+ initial_secret = initial_secret_buf;
+ }
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ if (version == NGTCP2_PROTO_VER_V1) {
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1;
+ } else {
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_hkdf_extract(initial_secret, &ctx.md, client_dcid->data,
+ client_dcid->datalen, salt, saltlen) != 0) {
+ return -1;
+ }
+
+ if (side == NGTCP2_CRYPTO_SIDE_SERVER) {
+ client_secret = rx_secret;
+ server_secret = tx_secret;
+ } else {
+ client_secret = tx_secret;
+ server_secret = rx_secret;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(
+ client_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md,
+ initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL,
+ sizeof(CLABEL) - 1) != 0 ||
+ ngtcp2_crypto_hkdf_expand_label(
+ server_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md,
+ initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL,
+ sizeof(SLABEL) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) {
+ size_t noncelen = ngtcp2_crypto_aead_noncelen(aead);
+ return ngtcp2_max(8, noncelen);
+}
+
+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";
+ size_t keylen = ngtcp2_crypto_aead_keylen(aead);
+ size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+
+ if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen,
+ KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen,
+ IV_LABEL, sizeof(IV_LABEL) - 1) != 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) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_update_traffic_secret(uint8_t *dest,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen) {
+ static const uint8_t LABEL[] = "quic ku";
+
+ if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen,
+ LABEL, sizeof(LABEL) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
+ uint8_t *iv, uint8_t *hp_key,
+ ngtcp2_crypto_level level,
+ const uint8_t *secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx;
+ const ngtcp2_crypto_aead *aead;
+ const ngtcp2_crypto_md *md;
+ const ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ uint8_t keybuf[64], ivbuf[64], hp_keybuf[64];
+ size_t ivlen;
+ int rv;
+ ngtcp2_crypto_ctx cctx;
+
+ if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) {
+ return 0;
+ }
+
+ if (!key) {
+ key = keybuf;
+ }
+ if (!iv) {
+ iv = ivbuf;
+ }
+ if (!hp_key) {
+ hp_key = hp_keybuf;
+ }
+
+ if (level == 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 {
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+
+ if (!ctx->aead.native_handle) {
+ ngtcp2_crypto_ctx_tls(&cctx, tls);
+ ngtcp2_conn_set_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ }
+ }
+
+ aead = &ctx->aead;
+ md = &ctx->md;
+ 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) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, aead, key, ivlen) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) {
+ goto fail;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, iv, ivlen,
+ &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ if (!ngtcp2_conn_is_server(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ rv = ngtcp2_conn_install_rx_key(conn, secret, secretlen, &aead_ctx, iv,
+ ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return -1;
+}
+
+/*
+ * crypto_set_local_transport_params gets local QUIC transport
+ * parameters from |conn| and sets it to |tls|.
+ *
+ * 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);
+ if (nwrite < 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
+ uint8_t *iv, uint8_t *hp_key,
+ ngtcp2_crypto_level level,
+ const uint8_t *secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx;
+ const ngtcp2_crypto_aead *aead;
+ const ngtcp2_crypto_md *md;
+ const ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ uint8_t keybuf[64], ivbuf[64], hp_keybuf[64];
+ size_t ivlen;
+ int rv;
+ ngtcp2_crypto_ctx cctx;
+
+ if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) {
+ return 0;
+ }
+
+ if (!key) {
+ key = keybuf;
+ }
+ if (!iv) {
+ iv = ivbuf;
+ }
+ if (!hp_key) {
+ hp_key = hp_keybuf;
+ }
+
+ if (level == 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 {
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+
+ if (!ctx->aead.native_handle) {
+ ngtcp2_crypto_ctx_tls(&cctx, tls);
+ ngtcp2_conn_set_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ }
+ }
+
+ aead = &ctx->aead;
+ md = &ctx->md;
+ 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) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, aead, key, ivlen) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) {
+ goto fail;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen,
+ &hp_ctx);
+ if (rv != 0) {
+ 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;
+ }
+ }
+
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ rv = ngtcp2_conn_install_tx_key(conn, secret, secretlen, &aead_ctx, iv,
+ ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return -1;
+}
+
+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) {
+ 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];
+ ngtcp2_crypto_ctx ctx;
+ ngtcp2_crypto_aead retry_aead;
+ 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};
+ 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;
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ 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;
+ }
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx);
+
+ 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, &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,
+ 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;
+ }
+
+ if (!server && !ngtcp2_conn_after_retry(conn)) {
+ ngtcp2_crypto_aead_retry(&retry_aead);
+
+ if (ngtcp2_conn_get_negotiated_version(conn) == NGTCP2_PROTO_VER_V1) {
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ } else {
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&retry_aead_ctx, &retry_aead,
+ retry_key, retry_noncelen) != 0) {
+ goto fail;
+ }
+ }
+
+ rv = ngtcp2_conn_install_initial_key(conn, &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;
+ }
+
+ if (retry_aead_ctx.native_handle) {
+ ngtcp2_conn_set_retry_aead(conn, &retry_aead, &retry_aead_ctx);
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_aead_ctx_free(&retry_aead_ctx);
+ 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,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ const ngtcp2_crypto_aead *aead = &ctx->aead;
+ const ngtcp2_crypto_md *md = &ctx->md;
+ size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+
+ 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) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_update_traffic_secret(tx_secret, md, current_tx_secret,
+ secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md,
+ tx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(rx_aead_ctx, aead, rx_key, ivlen) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(tx_aead_ctx, aead, tx_key, ivlen) !=
+ 0) {
+ ngtcp2_crypto_aead_ctx_free(rx_aead_ctx);
+ rx_aead_ctx->native_handle = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+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) {
+ if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen,
+ nonce, noncelen, ad, adlen) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+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) {
+ if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen,
+ nonce, noncelen, ad, adlen) != 0) {
+ return NGTCP2_ERR_TLS_DECRYPT;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_update_key_cb(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data) {
+ uint8_t rx_key[64];
+ uint8_t tx_key[64];
+ (void)conn;
+ (void)user_data;
+
+ if (ngtcp2_crypto_update_key(conn, rx_secret, tx_secret, rx_aead_ctx, rx_key,
+ rx_iv, tx_aead_ctx, tx_key, tx_iv,
+ current_rx_secret, current_tx_secret,
+ secretlen) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+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];
+ int rv;
+
+ assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf));
+ assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf));
+
+ 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);
+
+ 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) {
+ uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ ngtcp2_crypto_ctx ctx;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ if (ngtcp2_crypto_derive_initial_secrets(version, rx_secret, tx_secret,
+ initial_secret, scid,
+ NGTCP2_CRYPTO_SIDE_SERVER) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &ctx.aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ spktlen = -1;
+ goto end;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, &ctx.hp, tx_hp_key) != 0) {
+ spktlen = -1;
+ goto end;
+ }
+
+ 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);
+ if (spktlen < 0) {
+ spktlen = -1;
+ }
+
+end:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return spktlen;
+}
+
+ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
+ uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid,
+ const ngtcp2_cid *odcid,
+ const uint8_t *token, size_t tokenlen) {
+ ngtcp2_crypto_aead aead;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ const uint8_t *key;
+ size_t noncelen;
+
+ ngtcp2_crypto_aead_retry(&aead);
+
+ if (version == NGTCP2_PROTO_VER_V1) {
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ } else {
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, noncelen) !=
+ 0) {
+ return -1;
+ }
+
+ spktlen = ngtcp2_pkt_write_retry(dest, destlen, version, dcid, scid, odcid,
+ token, tokenlen, ngtcp2_crypto_encrypt_cb,
+ &aead, &aead_ctx);
+ if (spktlen < 0) {
+ spktlen = -1;
+ }
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ 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) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (crypto_set_local_transport_params(conn, tls) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (ngtcp2_crypto_read_write_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL,
+ NULL, 0) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+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) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid,
+ void *user_data) {
+ (void)user_data;
+
+ if (crypto_setup_initial_crypto(conn, dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+void ngtcp2_crypto_delete_crypto_aead_ctx_cb(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ngtcp2_crypto_aead_ctx_free(aead_ctx);
+}
+
+void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ngtcp2_crypto_cipher_ctx_free(cipher_ctx);
+}
diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.h b/deps/ngtcp2/ngtcp2/crypto/shared.h
new file mode 100644
index 0000000000..b7fe2f15da
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/crypto/shared.h
@@ -0,0 +1,211 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_SHARED_H
+#define NGTCP2_SHARED_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2_crypto.h>
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_DRAFT` is a salt value which is used to
+ * derive initial secret. It is used for QUIC draft versions.
+ */
+#define NGTCP2_INITIAL_SALT_DRAFT \
+ "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90" \
+ "\xa8\x99"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to
+ * derive initial secret. It is used for QUIC v1.
+ */
+#define NGTCP2_INITIAL_SALT_V1 \
+ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \
+ "\x7f\x0a"
+
+/* Maximum key usage (encryption) limits */
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM (2965820ULL)
+
+/* Maximum authentication failure (decryption) limits during the
+ lifetime of a connection. */
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM (1ULL << 52)
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36)
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL)
+
+/**
+ * @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
+ * writes rx and tx secrets into |rx_secret| and |tx_secret|
+ * respectively. The length of secret is 32 bytes long.
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client. If |initial_secret| is not NULL, the initial
+ * secret is written to it. It must point to the buffer which has at
+ * least 32 bytes capacity. The initial secret is 32 bytes long.
+ * |side| specifies the side of application.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
+ uint8_t *tx_secret,
+ uint8_t *initial_secret,
+ const ngtcp2_cid *client_dcid,
+ ngtcp2_crypto_side side);
+
+/**
+ * @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
+ * same as the current key. This function writes new key into the
+ * buffer pointed by |dest|. |dest| must have the enough capacity to
+ * store the new key.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_update_traffic_secret(uint8_t *dest,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_set_local_transport_params` sets QUIC transport
+ * parameter, which is encoded in wire format and stored in the buffer
+ * pointed by |buf| of length |len|, to the native handle |tls|.
+ *
+ * |tls| points to a implementation dependent TLS session object. If
+ * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL
+ * object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_set_remote_transport_params` retrieves a remote QUIC
+ * transport parameters from |tls| and sets it to |conn| using
+ * `ngtcp2_conn_set_remote_transport_params`.
+ *
+ * |tls| points to a implementation dependent TLS session object. If
+ * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL
+ * object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_initial_key` derives initial
+ * keying materials and installs keys to |conn|.
+ *
+ * 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 calls `ngtcp2_conn_set_initial_crypto_ctx` to set
+ * initial AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_initial_crypto_ctx` to get the object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+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,
+ const ngtcp2_cid *client_dcid);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_cipher_ctx_encrypt_init` initializes |cipher_ctx|
+ * with new cipher context object for encryption which is constructed
+ * to use |key| as encryption key. |cipher| specifies cipher to use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_cipher_ctx_free` frees up resources used by
+ * |cipher_ctx|. This function does not free the memory pointed by
+ * |cipher_ctx| itself.
+ */
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx);
+
+#endif /* NGTCP2_SHARED_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
new file mode 100644
index 0000000000..8a37bebda6
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
@@ -0,0 +1,4831 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2017 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_H
+#define NGTCP2_H
+
+/* Define WIN32 when build target is Win32 API (borrowed from
+ libcurl) */
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+/* MSVC < 2013 does not have inttypes.h because it is not C99
+ compliant. See compiler macros and version number in
+ https://sourceforge.net/p/predef/wiki/Compilers/ */
+# include <stdint.h>
+#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+# include <inttypes.h>
+#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef WIN32
+# include <winsock2.h>
+#else
+# include <sys/socket.h>
+#endif
+
+#include <ngtcp2/version.h>
+
+#ifdef NGTCP2_STATICLIB
+# define NGTCP2_EXTERN
+#elif defined(WIN32)
+# ifdef BUILDING_NGTCP2
+# define NGTCP2_EXTERN __declspec(dllexport)
+# else /* !BUILDING_NGTCP2 */
+# define NGTCP2_EXTERN __declspec(dllimport)
+# endif /* !BUILDING_NGTCP2 */
+#else /* !defined(WIN32) */
+# ifdef BUILDING_NGTCP2
+# define NGTCP2_EXTERN __attribute__((visibility("default")))
+# else /* !BUILDING_NGTCP2 */
+# define NGTCP2_EXTERN
+# endif /* !BUILDING_NGTCP2 */
+#endif /* !defined(WIN32) */
+
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_ssize` is signed counterpart of size_t.
+ */
+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.
+ */
+typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data);
+
+/**
+ * @functypedef
+ *
+ * Custom memory allocator to replace free(). The |mem_user_data| is
+ * the mem_user_data member of :type:`ngtcp2_mem` structure.
+ */
+typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data);
+
+/**
+ * @functypedef
+ *
+ * Custom memory allocator to replace calloc(). The |mem_user_data|
+ * is the mem_user_data member of :type:`ngtcp2_mem` structure.
+ */
+typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data);
+
+/**
+ * @functypedef
+ *
+ * Custom memory allocator to replace realloc(). The |mem_user_data|
+ * is the mem_user_data member of :type:`ngtcp2_mem` structure.
+ */
+typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_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.
+ *
+ * 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::
+ *
+ * void *my_malloc_cb(size_t size, void *mem_user_data) {
+ * return my_malloc(size);
+ * }
+ *
+ * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }
+ *
+ * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {
+ * return my_calloc(nmemb, size);
+ * }
+ *
+ * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {
+ * return my_realloc(ptr, size);
+ * }
+ *
+ * void conn_new() {
+ * ngtcp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,
+ * my_realloc_cb};
+ *
+ * ...
+ * }
+ */
+typedef struct ngtcp2_mem {
+ /**
+ * An arbitrary user supplied data. This is passed to each
+ * allocator function.
+ */
+ void *mem_user_data;
+ /**
+ * Custom allocator function to replace malloc().
+ */
+ ngtcp2_malloc malloc;
+ /**
+ * Custom allocator function to replace free().
+ */
+ ngtcp2_free free;
+ /**
+ * Custom allocator function to replace calloc().
+ */
+ ngtcp2_calloc calloc;
+ /**
+ * Custom allocator function to replace realloc().
+ */
+ ngtcp2_realloc realloc;
+} ngtcp2_mem;
+
+/**
+ * @macrosection
+ *
+ * Time related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 second.
+ */
+#define NGTCP2_SECONDS ((uint64_t)1000000000ULL)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds
+ * to 1 millisecond.
+ */
+#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds
+ * to 1 microsecond.
+ */
+#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to
+ * 1 nanosecond.
+ */
+#define NGTCP2_NANOSECONDS ((uint64_t)1ULL)
+
+/**
+ * @macrosection
+ *
+ * QUIC protocol version macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1.
+ */
+#define NGTCP2_PROTO_VER_V1 0x00000001u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_DRAFT_MAX` is the maximum QUIC draft
+ * version that this library supports.
+ */
+#define NGTCP2_PROTO_VER_DRAFT_MAX 0xff000020u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_DRAFT_MIN` is the minimum QUIC draft
+ * version that this library supports.
+ */
+#define NGTCP2_PROTO_VER_DRAFT_MIN 0xff00001du
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_MAX` is the highest QUIC version that this
+ * library supports.
+ */
+#define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER_V1
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_MIN` is the lowest QUIC version that this
+ * library supports.
+ */
+#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.
+ */
+#define NGTCP2_MAX_PKTLEN_IPV4 1252
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_PKTLEN_IPV6` is the maximum datagram size of
+ * IPv6 packet without PMTUD.
+ */
+#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.
+ */
+#define NGTCP2_MIN_INITIAL_PKTLEN 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.
+ */
+#define NGTCP2_DEFAULT_MAX_PKTLEN 1200
+
+/**
+ * @macrosection
+ *
+ * QUIC specific macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_VARINT` is the maximum value which can be
+ * encoded in variable-length integer encoding.
+ */
+#define NGTCP2_MAX_VARINT ((1ULL << 62) - 1)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` is the length of Stateless
+ * Reset Token.
+ */
+#define NGTCP2_STATELESS_RESET_TOKENLEN 16
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` is the minimum length
+ * of random bytes (Unpredictable Bits) in Stateless Retry packet
+ */
+#define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_KEY_DRAFT` is an encryption key to create
+ * integrity tag of Retry packet. It is used for QUIC draft versions.
+ */
+#define NGTCP2_RETRY_KEY_DRAFT \
+ "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_NONCE_DRAFT` is nonce used when generating
+ * integrity tag of Retry packet. It is used for QUIC draft versions.
+ */
+#define NGTCP2_RETRY_NONCE_DRAFT \
+ "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_KEY_V1` is an encryption key to create
+ * integrity tag of Retry packet. It is used for QUIC v1.
+ */
+#define NGTCP2_RETRY_KEY_V1 \
+ "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_NONCE_V1` is nonce used when generating integrity
+ * tag of Retry packet. It is used for QUIC v1.
+ */
+#define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_HP_MASKLEN` is the length of header protection mask.
+ */
+#define NGTCP2_HP_MASKLEN 5
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_HP_SAMPLELEN` is the number bytes sampled when
+ * encrypting a packet header.
+ */
+#define NGTCP2_HP_SAMPLELEN 16
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` is a default initial RTT.
+ */
+#define NGTCP2_DEFAULT_INITIAL_RTT (333 * NGTCP2_MILLISECONDS)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_CIDLEN` is the maximum length of Connection ID.
+ */
+#define NGTCP2_MAX_CIDLEN 20
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MIN_CIDLEN` is the minimum length of Connection ID.
+ */
+#define NGTCP2_MIN_CIDLEN 1
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN` is the minimum length of
+ * Destination Connection ID in Client Initial packet if it does not
+ * bear token from Retry packet.
+ */
+#define NGTCP2_MIN_INITIAL_DCIDLEN 8
+
+/**
+ * @macrosection
+ *
+ * ECN related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_NOT_ECT` indicates no ECN marking.
+ */
+#define NGTCP2_ECN_NOT_ECT 0x0
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_ECT_1` is ECT(1) codepoint.
+ */
+#define NGTCP2_ECN_ECT_1 0x1
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_ECT_0` is ECT(0) codepoint.
+ */
+#define NGTCP2_ECN_ECT_0 0x2
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_CE` is CE codepoint.
+ */
+#define NGTCP2_ECN_CE 0x3
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_MASK` is a bit mask to get ECN marking.
+ */
+#define NGTCP2_ECN_MASK 0x3
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_pkt_info` is a packet metadata.
+ */
+typedef struct ngtcp2_pkt_info {
+ /**
+ * :member:`ecn <ngtcp2_pkt_info.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`.
+ */
+ uint32_t ecn;
+} ngtcp2_pkt_info;
+
+/**
+ * @macrosection
+ *
+ * ngtcp2 library error codes
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` indicates that a passed
+ * argument is invalid.
+ */
+#define NGTCP2_ERR_INVALID_ARGUMENT -201
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_NOBUF` indicates that a provided buffer does not
+ * have enough space to store data.
+ */
+#define NGTCP2_ERR_NOBUF -203
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_PROTO` indicates a general protocol error.
+ */
+#define NGTCP2_ERR_PROTO -205
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE` indicates that a requested
+ * operation is not allowed at the current connection state.
+ */
+#define NGTCP2_ERR_INVALID_STATE -206
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_ACK_FRAME` indicates that an invalid ACK frame
+ * is received.
+ */
+#define NGTCP2_ERR_ACK_FRAME -207
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` indicates that there is no
+ * spare stream ID available.
+ */
+#define NGTCP2_ERR_STREAM_ID_BLOCKED -208
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_IN_USE` indicates that a stream ID is
+ * already in use.
+ */
+#define NGTCP2_ERR_STREAM_IN_USE -209
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` indicates that stream data
+ * cannot be sent because of flow control.
+ */
+#define NGTCP2_ERR_STREAM_DATA_BLOCKED -210
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_FLOW_CONTROL` indicates flow control error.
+ */
+#define NGTCP2_ERR_FLOW_CONTROL -211
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CONNECTION_ID_LIMIT` indicates that the number
+ * of received Connection ID exceeds acceptable limit.
+ */
+#define NGTCP2_ERR_CONNECTION_ID_LIMIT -212
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_LIMIT` indicates that a remote endpoint
+ * opens more streams that is permitted.
+ */
+#define NGTCP2_ERR_STREAM_LIMIT -213
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_FINAL_SIZE` indicates that inconsistent final
+ * size of a stream.
+ */
+#define NGTCP2_ERR_FINAL_SIZE -214
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CRYPTO` indicates crypto (TLS) related error.
+ */
+#define NGTCP2_ERR_CRYPTO -215
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` indicates that packet number
+ * is exhausted.
+ */
+#define NGTCP2_ERR_PKT_NUM_EXHAUSTED -216
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` indicates that a
+ * required transport parameter is missing.
+ */
+#define NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM -217
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` indicates that a
+ * transport parameter is malformed.
+ */
+#define NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM -218
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_FRAME_ENCODING` indicates there is an error in
+ * frame encoding.
+ */
+#define NGTCP2_ERR_FRAME_ENCODING -219
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_TLS_DECRYPT` indicates TLS decryption failure.
+ */
+#define NGTCP2_ERR_TLS_DECRYPT -220
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_SHUT_WR` indicates no more data can be
+ * sent to a stream.
+ */
+#define NGTCP2_ERR_STREAM_SHUT_WR -221
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` indicates that a stream was not
+ * found.
+ */
+#define NGTCP2_ERR_STREAM_NOT_FOUND -222
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_STATE` indicates that a requested
+ * operation is not allowed at the current stream state.
+ */
+#define NGTCP2_ERR_STREAM_STATE -226
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION` indicates that Version
+ * Negotiation packet was received.
+ */
+#define NGTCP2_ERR_RECV_VERSION_NEGOTIATION -229
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CLOSING` indicates that connection is in closing
+ * state.
+ */
+#define NGTCP2_ERR_CLOSING -230
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_DRAINING` indicates that connection is in
+ * draining state.
+ */
+#define NGTCP2_ERR_DRAINING -231
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` indicates a general transport
+ * parameter error.
+ */
+#define NGTCP2_ERR_TRANSPORT_PARAM -234
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_DISCARD_PKT` indicates a packet was discarded.
+ */
+#define NGTCP2_ERR_DISCARD_PKT -235
+/**
+ * @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.
+ */
+#define NGTCP2_ERR_CONN_ID_BLOCKED -237
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_INTERNAL` indicates an internal error.
+ */
+#define NGTCP2_ERR_INTERNAL -238
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED` indicates that a crypto
+ * buffer exceeded.
+ */
+#define NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED -239
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_WRITE_MORE` indicates
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used and a function call
+ * succeeded.
+ */
+#define NGTCP2_ERR_WRITE_MORE -240
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_RETRY` indicates that server should send Retry
+ * packet.
+ */
+#define NGTCP2_ERR_RETRY -241
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_DROP_CONN` indicates that an endpoint should
+ * drop connection immediately.
+ */
+#define NGTCP2_ERR_DROP_CONN -242
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_AEAD_LIMIT_REACHED` indicates AEAD encryption
+ * limit is reached and key update is not available. An endpoint
+ * should drop connection immediately.
+ */
+#define NGTCP2_ERR_AEAD_LIMIT_REACHED -243
+/**
+ * @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.
+ */
+#define NGTCP2_ERR_NO_VIABLE_PATH -244
+/**
+ * @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.
+ */
+#define NGTCP2_ERR_FATAL -500
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_NOMEM` indicates out of memory.
+ */
+#define NGTCP2_ERR_NOMEM -501
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` indicates that user defined
+ * callback function failed.
+ */
+#define NGTCP2_ERR_CALLBACK_FAILURE -502
+
+/**
+ * @macrosection
+ *
+ * QUIC packet header flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_PKT_FLAG_NONE 0
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long packet
+ * header.
+ */
+#define NGTCP2_PKT_FLAG_LONG_FORM 0x01
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set.
+ */
+#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_pkt_type` defines QUIC packet types.
+ */
+typedef enum ngtcp2_pkt_type {
+ /**
+ * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` is defined by libngtcp2
+ * for convenience.
+ */
+ NGTCP2_PKT_VERSION_NEGOTIATION = 0xf0,
+ /**
+ * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet.
+ */
+ NGTCP2_PKT_INITIAL = 0x0,
+ /**
+ * :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet.
+ */
+ NGTCP2_PKT_0RTT = 0x1,
+ /**
+ * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet.
+ */
+ NGTCP2_PKT_HANDSHAKE = 0x2,
+ /**
+ * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet.
+ */
+ NGTCP2_PKT_RETRY = 0x3,
+ /**
+ * :enum:`NGTCP2_PKT_SHORT` is defined by libngtcp2 for convenience.
+ */
+ NGTCP2_PKT_SHORT = 0x70
+} ngtcp2_pkt_type;
+
+/**
+ * @macrosection
+ *
+ * QUIC transport error code
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_NO_ERROR` is QUIC transport error code ``NO_ERROR``.
+ */
+#define NGTCP2_NO_ERROR 0x0u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INTERNAL_ERROR` is QUIC transport error code
+ * ``INTERNAL_ERROR``.
+ */
+#define NGTCP2_INTERNAL_ERROR 0x1u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CONNECTION_REFUSED` is QUIC transport error code
+ * ``CONNECTION_REFUSED``.
+ */
+#define NGTCP2_CONNECTION_REFUSED 0x2u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_FLOW_CONTROL_ERROR` is QUIC transport error code
+ * ``FLOW_CONTROL_ERROR``.
+ */
+#define NGTCP2_FLOW_CONTROL_ERROR 0x3u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_LIMIT_ERROR` is QUIC transport error code
+ * ``STREAM_LIMIT_ERROR``.
+ */
+#define NGTCP2_STREAM_LIMIT_ERROR 0x4u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_STATE_ERROR` is QUIC transport error code
+ * ``STREAM_STATE_ERROR``.
+ */
+#define NGTCP2_STREAM_STATE_ERROR 0x5u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_FINAL_SIZE_ERROR` is QUIC transport error code
+ * ``FINAL_SIZE_ERROR``.
+ */
+#define NGTCP2_FINAL_SIZE_ERROR 0x6u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_FRAME_ENCODING_ERROR` is QUIC transport error code
+ * ``FRAME_ENCODING_ERROR``.
+ */
+#define NGTCP2_FRAME_ENCODING_ERROR 0x7u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_TRANSPORT_PARAMETER_ERROR` is QUIC transport error
+ * code ``TRANSPORT_PARAMETER_ERROR``.
+ */
+#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CONNECTION_ID_LIMIT_ERROR` is QUIC transport error
+ * code ``CONNECTION_ID_LIMIT_ERROR``.
+ */
+#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTOCOL_VIOLATION` is QUIC transport error code
+ * ``PROTOCOL_VIOLATION``.
+ */
+#define NGTCP2_PROTOCOL_VIOLATION 0xau
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INVALID_TOKEN` is QUIC transport error code
+ * ``INVALID_TOKEN``.
+ */
+#define NGTCP2_INVALID_TOKEN 0xbu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_APPLICATION_ERROR` is QUIC transport error code
+ * ``APPLICATION_ERROR``.
+ */
+#define NGTCP2_APPLICATION_ERROR 0xcu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_BUFFER_EXCEEDED` is QUIC transport error code
+ * ``CRYPTO_BUFFER_EXCEEDED``.
+ */
+#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_KEY_UPDATE_ERROR` is QUIC transport error code
+ * ``KEY_UPDATE_ERROR``.
+ */
+#define NGTCP2_KEY_UPDATE_ERROR 0xeu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_AEAD_LIMIT_REACHED` is QUIC transport error code
+ * ``AEAD_LIMIT_REACHED``.
+ */
+#define NGTCP2_AEAD_LIMIT_REACHED 0xfu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_NO_VIABLE_PATH` is QUIC transport error code
+ * ``NO_VIABLE_PATH``.
+ */
+#define NGTCP2_NO_VIABLE_PATH 0x10u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_ERROR` is QUIC transport error code
+ * ``CRYPTO_ERROR``.
+ */
+#define NGTCP2_CRYPTO_ERROR 0x100u
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_path_validation_result` defines path validation
+ * result code.
+ */
+typedef enum ngtcp2_path_validation_result {
+ /**
+ * :enum:`NGTCP2_PATH_VALIDATION_RESULT_SUCCESS` indicates
+ * successful validation.
+ */
+ NGTCP2_PATH_VALIDATION_RESULT_SUCCESS,
+ /**
+ * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE` indicates
+ * validation failure.
+ */
+ NGTCP2_PATH_VALIDATION_RESULT_FAILURE
+} ngtcp2_path_validation_result;
+
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_tstamp` is a timestamp with nanosecond resolution.
+ */
+typedef uint64_t ngtcp2_tstamp;
+
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_duration` is a period of time in nanosecond
+ * resolution.
+ */
+typedef uint64_t ngtcp2_duration;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cid` holds a Connection ID.
+ */
+typedef struct ngtcp2_cid {
+ /**
+ * :member:`datalen <ngtcp2_cid.datalen>` is the length of
+ * Connection ID.
+ */
+ size_t datalen;
+ /**
+ * :member:`data <ngtcp2_cid.data>` is the buffer to store
+ * Connection ID.
+ */
+ uint8_t data[NGTCP2_MAX_CIDLEN];
+} ngtcp2_cid;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_vec` is struct iovec compatible structure to
+ * reference arbitrary array of bytes.
+ */
+typedef struct ngtcp2_vec {
+ /**
+ * :member:`base <ngtcp2_vec.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.
+ */
+ size_t len;
+} ngtcp2_vec;
+
+/**
+ * @function
+ *
+ * `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`.
+ */
+NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data,
+ size_t datalen);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_pkt_hd` represents QUIC packet header.
+ */
+typedef struct ngtcp2_pkt_hd {
+ /**
+ * :member:`dcid` is Destination Connection ID.
+ */
+ ngtcp2_cid dcid;
+ /**
+ * :member:`scid` is Source Connection ID.
+ */
+ ngtcp2_cid scid;
+ /**
+ * :member:`pkt_num` is a packet number.
+ */
+ int64_t pkt_num;
+ /**
+ * :member:`token` contains token for Initial
+ * packet.
+ */
+ ngtcp2_vec token;
+ /**
+ * :member:`pkt_numlen` is the number of bytes spent to encode
+ * :member:`pkt_num`.
+ */
+ size_t pkt_numlen;
+ /**
+ * :member:`len` is the sum of :member:`pkt_numlen` and the length
+ * of QUIC packet payload.
+ */
+ size_t len;
+ /**
+ * :member:`version` is QUIC version.
+ */
+ uint32_t version;
+ /**
+ * :member:`type` is a type of QUIC packet. See
+ * :type:`ngtcp2_pkt_type`.
+ */
+ uint8_t type;
+ /**
+ * :member:`flags` is zero or more of NGTCP2_PKT_FLAG_*. See
+ * :macro:`NGTCP2_PKT_FLAG_NONE`.
+ */
+ uint8_t flags;
+} ngtcp2_pkt_hd;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_pkt_stateless_reset` represents Stateless Reset.
+ */
+typedef struct ngtcp2_pkt_stateless_reset {
+ /**
+ * :member:`stateless_reset_token` contains stateless reset token.
+ */
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ /**
+ * :member:`rand` points a buffer which contains random bytes
+ * section.
+ */
+ const uint8_t *rand;
+ /**
+ * :member:`randlen` is the number of random bytes.
+ */
+ 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
+ *
+ * :type:`ngtcp2_transport_params_type` defines TLS message type which
+ * carries transport parameters.
+ */
+typedef enum ngtcp2_transport_params_type {
+ /**
+ * :enum:`NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO` is Client Hello
+ * TLS message.
+ */
+ NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
+ /**
+ * :enum:`NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS` is
+ * Encrypted Extensions TLS message.
+ */
+ NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
+} 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
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` is the default value
+ * of max_udp_payload_size transport parameter.
+ */
+#define NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE 65527
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` is a default value of
+ * scaling factor of ACK Delay field in ACK frame.
+ */
+#define NGTCP2_DEFAULT_ACK_DELAY_EXPONENT 3
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY` is a default value of the
+ * maximum amount of time in nanoseconds by which endpoint delays
+ * sending acknowledgement.
+ */
+#define NGTCP2_DEFAULT_MAX_ACK_DELAY (25 * NGTCP2_MILLISECONDS)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` is the default
+ * value of active_connection_id_limit transport parameter value if
+ * omitted.
+ */
+#define NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT 2
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS` is TLS extension
+ * type of quic_transport_parameters.
+ */
+#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS 0xffa5u
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_preferred_addr` represents preferred address
+ * structure.
+ */
+typedef struct ngtcp2_preferred_addr {
+ /**
+ * :member:`cid` is a Connection ID.
+ */
+ ngtcp2_cid cid;
+ /**
+ * :member:`ipv4_port` is a port of IPv4 address.
+ */
+ uint16_t ipv4_port;
+ /**
+ * :member:`ipv6_port` is a port of IPv6 address.
+ */
+ uint16_t ipv6_port;
+ /**
+ * :member:`ipv4_addr` contains IPv4 address in network byte order.
+ */
+ uint8_t ipv4_addr[4];
+ /**
+ * :member:`ipv6_addr` contains IPv6 address in network byte order.
+ */
+ uint8_t ipv6_addr[16];
+ /**
+ * :member:`stateless_reset_token` contains stateless reset token.
+ */
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+} ngtcp2_preferred_addr;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_transport_params` represents QUIC transport
+ * parameters.
+ */
+typedef struct ngtcp2_transport_params {
+ /**
+ * :member:`preferred_address` contains preferred address if
+ * :member:`preferred_address_present` is nonzero.
+ */
+ ngtcp2_preferred_addr preferred_address;
+ /**
+ * :member:`original_dcid` is the Destination Connection ID field
+ * from the first Initial packet from client. Server must specify
+ * this field. It is expected that application knows the original
+ * Destination Connection ID even if it sends Retry packet, for
+ * example, by including it in retry token. Otherwise, application
+ * should not specify this field.
+ */
+ ngtcp2_cid original_dcid;
+ /**
+ * :member:`initial_scid` is the Source Connection ID field from the
+ * first Initial packet the endpoint sends. Application should not
+ * specify this field.
+ */
+ ngtcp2_cid initial_scid;
+ /**
+ * :member:`retry_scid` is the Source Connection ID field from Retry
+ * packet. Only server uses this field. If server application
+ * received Initial packet with retry token from client and server
+ * verified its token, server application must set Destination
+ * Connection ID field from the Initial packet to this field and set
+ * :member:`retry_scid_present` to nonzero. Server application must
+ * verify that the Destination Connection ID from Initial packet was
+ * sent in Retry packet by, for example, including the Connection ID
+ * in a token, or including it in AAD when encrypting a token.
+ */
+ ngtcp2_cid retry_scid;
+ /**
+ * :member:`initial_max_stream_data_bidi_local` is the size of flow
+ * control window of locally initiated stream. This is the number
+ * of bytes that the remote endpoint can send and the local endpoint
+ * must ensure that it has enough buffer to receive them.
+ */
+ uint64_t initial_max_stream_data_bidi_local;
+ /**
+ * :member:`initial_max_stream_data_bidi_remote` is the size of flow
+ * control window of remotely initiated stream. This is the number
+ * of bytes that the remote endpoint can send and the local endpoint
+ * must ensure that it has enough buffer to receive them.
+ */
+ uint64_t initial_max_stream_data_bidi_remote;
+ /**
+ * :member:`initial_max_stream_data_uni` is the size of flow control
+ * window of remotely initiated unidirectional stream. This is the
+ * number of bytes that the remote endpoint can send and the local
+ * endpoint must ensure that it has enough buffer to receive them.
+ */
+ uint64_t initial_max_stream_data_uni;
+ /**
+ * :member:`initial_max_data` is the connection level flow control
+ * window.
+ */
+ uint64_t initial_max_data;
+ /**
+ * :member:`initial_max_streams_bidi` is the number of concurrent
+ * streams that the remote endpoint can create.
+ */
+ uint64_t initial_max_streams_bidi;
+ /**
+ * :member:`initial_max_streams_uni` is the number of concurrent
+ * unidirectional streams that the remote endpoint can create.
+ */
+ uint64_t initial_max_streams_uni;
+ /**
+ * :member:`max_idle_timeout` is a duration during which sender
+ * allows quiescent.
+ */
+ ngtcp2_duration max_idle_timeout;
+ /**
+ * :member:`max_udp_payload_size` is the maximum datagram size that
+ * the endpoint can receive.
+ */
+ uint64_t max_udp_payload_size;
+ /**
+ * :member:`active_connection_id_limit` is the maximum number of
+ * Connection ID that sender can store.
+ */
+ uint64_t active_connection_id_limit;
+ /**
+ * :member:`ack_delay_exponent` is the exponent used in ACK Delay
+ * field in ACK frame.
+ */
+ uint64_t ack_delay_exponent;
+ /**
+ * :member:`max_ack_delay` is the maximum acknowledgement delay by
+ * which the endpoint will delay sending acknowledgements.
+ */
+ ngtcp2_duration max_ack_delay;
+ /**
+ * :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
+ */
+ uint64_t max_datagram_frame_size;
+ /**
+ * :member:`stateless_reset_token_present` is nonzero if
+ * :member:`stateless_reset_token` field is set.
+ */
+ uint8_t stateless_reset_token_present;
+ /**
+ * :member:`disable_active_migration` is nonzero if the endpoint
+ * does not support active connection migration.
+ */
+ uint8_t disable_active_migration;
+ /**
+ * :member:`retry_scid_present` is nonzero if :member:`retry_scid`
+ * field is set.
+ */
+ uint8_t retry_scid_present;
+ /**
+ * :member:`preferred_address_present` is nonzero if
+ * :member:`preferred_address` is set.
+ */
+ uint8_t preferred_address_present;
+ /**
+ * :member:`stateless_reset_token` contains stateless reset token.
+ */
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+} 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.
+ */
+typedef enum ngtcp2_pktns_id {
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_INITIAL` is the Initial packet number
+ * space.
+ */
+ NGTCP2_PKTNS_ID_INITIAL,
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_HANDSHAKE` is the Handshake packet number
+ * space.
+ */
+ NGTCP2_PKTNS_ID_HANDSHAKE,
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_APPLICATION` is the Application data
+ * packet number space.
+ */
+ NGTCP2_PKTNS_ID_APPLICATION,
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_MAX` is defined to get the number of
+ * packet number spaces.
+ */
+ NGTCP2_PKTNS_ID_MAX
+} ngtcp2_pktns_id;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_conn_stat` holds various connection statistics, and
+ * computed data for recovery and congestion controller.
+ */
+typedef struct ngtcp2_conn_stat {
+ /**
+ * :member:`latest_rtt` is the latest RTT sample which is not
+ * adjusted by acknowledgement delay.
+ */
+ ngtcp2_duration latest_rtt;
+ /**
+ * :member:`min_rtt` is the minimum RTT seen so far. It is not
+ * adjusted by acknowledgement delay.
+ */
+ ngtcp2_duration min_rtt;
+ /**
+ * :member:`smoothed_rtt` is the smoothed RTT.
+ */
+ ngtcp2_duration smoothed_rtt;
+ /**
+ * :member:`rttvar` is a mean deviation of observed RTT.
+ */
+ ngtcp2_duration rttvar;
+ /**
+ * :member:`initial_rtt` is the initial RTT which is used when no
+ * RTT sample is available.
+ */
+ ngtcp2_duration initial_rtt;
+ /**
+ * :member:`first_rtt_sample_ts` is the timestamp when the first RTT
+ * sample is obtained.
+ */
+ ngtcp2_tstamp first_rtt_sample_ts;
+ /**
+ * :member:`pto_count` is the count of successive PTO timer
+ * expiration.
+ */
+ size_t pto_count;
+ /**
+ * :member:`loss_detection_timer` is the deadline of the current
+ * loss detection timer.
+ */
+ 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.
+ */
+ ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX];
+ /**
+ * :member:`loss_time` corresponds to loss_time in
+ * draft-ietf-quic-recovery-32.
+ */
+ ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX];
+ /**
+ * :member:`cwnd` is the size of congestion window.
+ */
+ uint64_t cwnd;
+ /**
+ * :member:`ssthresh` is slow start threshold.
+ */
+ uint64_t ssthresh;
+ /**
+ * :member:`congestion_recovery_start_ts` is the timestamp when
+ * congestion recovery started.
+ */
+ ngtcp2_tstamp congestion_recovery_start_ts;
+ /**
+ * :member:`bytes_in_flight` is the number in bytes of all sent
+ * packets which have not been acknowledged.
+ */
+ uint64_t bytes_in_flight;
+ /**
+ * :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.
+ */
+ size_t max_udp_payload_size;
+ /**
+ * :member:`delivery_rate_sec` is the current sending rate measured
+ * in byte per second.
+ */
+ uint64_t delivery_rate_sec;
+} ngtcp2_conn_stat;
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_cc_algo` defines congestion control algorithms.
+ */
+typedef enum ngtcp2_cc_algo {
+ /**
+ * :enum:`NGTCP2_CC_ALGO_RENO` represents Reno.
+ */
+ NGTCP2_CC_ALGO_RENO = 0x00,
+ /**
+ * :enum:`NGTCP2_CC_ALGO_CUBIC` represents Cubic.
+ */
+ 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).
+ */
+ ngtcp2_cc_congestion_event congestion_event;
+ /**
+ * :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;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_printf` is a callback function for logging.
+ * |user_data| is the same object passed to `ngtcp2_conn_client_new`
+ * or `ngtcp2_conn_server_new`.
+ */
+typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...);
+
+/**
+ * @macrosection
+ *
+ * QLog related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_QLOG_WRITE_FLAG_NONE 0
+/**
+ * @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
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_rand_ctx` is a wrapper around native random number
+ * generator. It is opaque to the ngtcp2 library. This might be
+ * useful if application needs to specify random number generator per
+ * thread or per connection.
+ */
+typedef struct ngtcp2_rand_ctx {
+ /**
+ * :member:`native_handle` is a pointer to an underlying random
+ * number generator.
+ */
+ void *native_handle;
+} ngtcp2_rand_ctx;
+
+/**
+ * @functypedef
+ *
+ * :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
+ * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0.
+ */
+typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags,
+ const void *data, size_t datalen);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_qlog_settings` is a set of settings for qlog.
+ */
+typedef struct ngtcp2_qlog_settings {
+ /**
+ * :member:`odcid` is Original Destination Connection ID sent by
+ * client. It is used as group_id and ODCID fields. Client ignores
+ * this field and uses dcid parameter passed to
+ * `ngtcp2_conn_client_new()`.
+ */
+ ngtcp2_cid odcid;
+ /**
+ * :member:`write` is a callback function to write qlog. Setting
+ * ``NULL`` disables qlog.
+ */
+ ngtcp2_qlog_write write;
+} ngtcp2_qlog_settings;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_settings` defines QUIC connection settings.
+ */
+typedef struct ngtcp2_settings {
+ /**
+ * :member:`qlog` is qlog 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.
+ */
+ 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.
+ */
+ ngtcp2_tstamp initial_ts;
+ /**
+ * :member:`initial_rtt` is an initial RTT.
+ */
+ ngtcp2_duration initial_rtt;
+ /**
+ * :member:`log_printf` is a function that the library uses to write
+ * logs. ``NULL`` means no logging output. It is nothing to do
+ * with qlog.
+ */
+ ngtcp2_printf log_printf;
+ /**
+ * :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`.
+ */
+ size_t max_udp_payload_size;
+ /**
+ * :member:`token` is a token from Retry packet or NEW_TOKEN frame.
+ *
+ * Server sets this field if it received the token in Client Initial
+ * packet and successfully validated.
+ *
+ * Client sets this field if it intends to send token in its Initial
+ * packet.
+ *
+ * `ngtcp2_conn_server_new` and `ngtcp2_conn_client_new` make a copy
+ * of token.
+ */
+ ngtcp2_vec token;
+ /**
+ * :member:`rand_ctx` is an optional random number generator to be
+ * passed to :type:`ngtcp2_rand` callback.
+ */
+ ngtcp2_rand_ctx rand_ctx;
+ /**
+ * :member:`max_window` is the maximum connection-level flow control
+ * window if connection-level window auto-tuning is enabled. The
+ * connection-level window auto tuning is enabled if nonzero value
+ * is specified in this field. The initial value of window size is
+ * :member:`ngtcp2_transport_params.initial_max_data`. The window
+ * size is scaled up to the value specified in this field.
+ */
+ uint64_t max_window;
+ /**
+ * :member:`max_stream_window` is the maximum stream-level flow
+ * control window if stream-level window auto-tuning is enabled.
+ * The stream-level window auto-tuning is enabled if nonzero value
+ * is specified in this field. The initial value of window size is
+ * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_remote`,
+ * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_local`,
+ * or :member:`ngtcp2_transport_params.initial_max_stream_data_uni`,
+ * depending on the type of stream. The window size is scaled up to
+ * the value specified in this field.
+ */
+ uint64_t max_stream_window;
+ /**
+ * :member:`ack_thresh` is the maximum number of unacknowledged
+ * packets before sending acknowledgement. It triggers the
+ * immediate acknowledgement.
+ */
+ size_t ack_thresh;
+} ngtcp2_settings;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_addr` is the endpoint address.
+ */
+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;
+ /**
+ * :member:`user_data` is an arbitrary data and opaque to the
+ * library.
+ */
+ void *user_data;
+} ngtcp2_addr;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_path` is the network endpoints where a packet is sent
+ * and received.
+ */
+typedef struct ngtcp2_path {
+ /**
+ * :member:`local` is the address of local endpoint.
+ */
+ ngtcp2_addr local;
+ /**
+ * :member:`remote` is the address of remote endpoint.
+ */
+ ngtcp2_addr remote;
+} ngtcp2_path;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_path_storage` is a convenient struct to have buffers
+ * to store the longest addresses.
+ */
+typedef struct ngtcp2_path_storage {
+ /**
+ * :member:`local_addrbuf` is a buffer to store local address.
+ */
+ struct sockaddr_storage local_addrbuf;
+ /**
+ * :member:`remote_addrbuf` is a buffer to store remote address.
+ */
+ struct sockaddr_storage remote_addrbuf;
+ /**
+ * :member:`path` stores network path.
+ */
+ ngtcp2_path path;
+} ngtcp2_path_storage;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_md` is a wrapper around native message digest
+ * object.
+ */
+typedef struct ngtcp2_crypto_md {
+ /**
+ * :member:`native_handle` is a pointer to an underlying message
+ * digest object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_md;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_aead` is a wrapper around native AEAD object.
+ */
+typedef struct ngtcp2_crypto_aead {
+ /**
+ * :member:`native_handle` is a pointer to an underlying AEAD
+ * object.
+ */
+ void *native_handle;
+ /**
+ * :member:`max_overhead` is the number of additional bytes which
+ * AEAD encryption needs on encryption.
+ */
+ size_t max_overhead;
+} ngtcp2_crypto_aead;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_cipher` is a wrapper around native cipher
+ * object.
+ */
+typedef struct ngtcp2_crypto_cipher {
+ /**
+ * :member:`native_handle` is a pointer to an underlying cipher
+ * object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_cipher;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_aead_ctx` is a wrapper around native AEAD
+ * cipher context object. It should be initialized with a specific
+ * key. ngtcp2 library reuses this context object to encrypt or
+ * decrypt multiple packets.
+ */
+typedef struct ngtcp2_crypto_aead_ctx {
+ /**
+ * :member:`native_handle` is a pointer to an underlying AEAD
+ * context object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_aead_ctx;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_cipher_ctx` is a wrapper around native cipher
+ * context object. It should be initialized with a specific key.
+ * ngtcp2 library reuses this context object to encrypt or decrypt
+ * multiple packet headers.
+ */
+typedef struct ngtcp2_crypto_cipher_ctx {
+ /**
+ * :member:`native_handle` is a pointer to an underlying cipher
+ * context object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_cipher_ctx;
+
+/**
+ * @struct
+ *
+ * :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
+ * `ngtcp2_crypto_ctx_tls`.
+ */
+typedef struct ngtcp2_crypto_ctx {
+ /**
+ * :member:`aead` is AEAD object.
+ */
+ ngtcp2_crypto_aead aead;
+ /**
+ * :member:`md` is message digest object.
+ */
+ ngtcp2_crypto_md md;
+ /**
+ * :member:`hp` is header protection cipher.
+ */
+ ngtcp2_crypto_cipher hp;
+ /**
+ * :member:`max_encryption` is the number of encryption which this
+ * key can be used with.
+ */
+ uint64_t max_encryption;
+ /**
+ * :member:`max_decryption_failure` is the number of decryption
+ * failure with this key.
+ */
+ uint64_t max_decryption_failure;
+} ngtcp2_crypto_ctx;
+
+/**
+ * @function
+ *
+ * `ngtcp2_encode_transport_params` encodes |params| in |dest| of
+ * length |destlen|.
+ *
+ * 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`:
+ * |exttype| is invalid.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params(
+ uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype,
+ const ngtcp2_transport_params *params);
+
+/**
+ * @function
+ *
+ * `ngtcp2_decode_transport_params` decodes transport parameters in
+ * |data| of length |datalen|, and stores the result in the object
+ * pointed by |params|.
+ *
+ * If the optional parameters are missing, the default value is
+ * assigned.
+ *
+ * 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_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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_decode_version_cid` extracts QUIC version, Destination
+ * Connection ID and Source Connection ID from the packet pointed by
+ * |data| of length |datalen|. This function can handle Connection ID
+ * up to 255 bytes unlike `ngtcp2_pkt_decode_hd_long` or
+ * `ngtcp2_pkt_decode_hd_short` which are only capable of handling
+ * Connection ID less than or equal to :macro:`NGTCP2_MAX_CIDLEN`.
+ * 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
+ * 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
+ * following negative error code:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The function could not decode the packet header.
+ */
+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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_decode_hd_long` decodes QUIC long packet header in
+ * |pkt| of length |pktlen|. This function only parses the input just
+ * before packet number field.
+ *
+ * This function does not verify that length field is correct. In
+ * other words, this function succeeds even if length > |pktlen|.
+ *
+ * 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.
+ *
+ * This function handles Version Negotiation specially. If version
+ * field is 0, |pkt| must contain Version Negotiation packet. Version
+ * 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.
+ *
+ * It stores the result in the object pointed by |dest|, and returns
+ * the number of bytes decoded to read the packet header if it
+ * succeeds, or one of the following error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * Packet is too short; or it is not a long header
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest,
+ const uint8_t *pkt,
+ size_t pktlen);
+
+/**
+ * @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
+ * :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
+ * number of bytes decoded to read the packet header if it succeeds,
+ * or one of the following error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * Packet is too short; or it is not a short header
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest,
+ const uint8_t *pkt,
+ size_t pktlen,
+ size_t dcidlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_write_stateless_reset` writes Stateless Reset packet in
+ * the buffer pointed by |dest| whose length is |destlen|.
+ * |stateless_reset_token| is a pointer to the Stateless Reset Token,
+ * 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
+ * long.
+ *
+ * If |randlen| is too long to write them all in the buffer, |rand| is
+ * written to the buffer as much as possible, and is truncated.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |randlen| is strictly less than
+ * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset(
+ uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token,
+ const uint8_t *rand, size_t randlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_write_version_negotiation` writes Version Negotiation
+ * packet in the buffer pointed by |dest| whose length is |destlen|.
+ * |unused_random| should be generated randomly. |dcid| is the
+ * destination connection ID which appears in a packet as a source
+ * connection ID sent by client which caused version negotiation.
+ * Similarly, |scid| is the source connection ID which appears in a
+ * packet as a destination connection ID sent by client. |sv| is a
+ * list of supported versions, and |nsv| specifies the number of
+ * supported versions included in |sv|.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_version_negotiation(
+ uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid,
+ size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv,
+ size_t nsv);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_conn` represents a single QUIC connection.
+ */
+typedef struct ngtcp2_conn ngtcp2_conn;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_client_initial` is invoked when client application
+ * asks TLS stack to produce first TLS cryptographic handshake data.
+ *
+ * This implementation of this callback must get the first handshake
+ * data from TLS stack and pass it to ngtcp2 library using
+ * `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
+ *
+ * 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);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_client_initial` is invoked when server receives
+ * 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
+ * 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,
+ void *user_data);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_crypto_level` is encryption level.
+ */
+typedef enum ngtcp2_crypto_level {
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_INITIAL` is Initial Keys encryption
+ * level.
+ */
+ NGTCP2_CRYPTO_LEVEL_INITIAL,
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_HANDSHAKE` is Handshake Keys
+ * encryption level.
+ */
+ NGTCP2_CRYPTO_LEVEL_HANDSHAKE,
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_APPLICATION` is Application Data
+ * (1-RTT) Keys encryption level.
+ */
+ NGTCP2_CRYPTO_LEVEL_APPLICATION,
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_EARLY` is Early Data (0-RTT) Keys
+ * encryption level.
+ */
+ NGTCP2_CRYPTO_LEVEL_EARLY
+} ngtcp2_crypto_level;
+
+/**
+ * @functypedef
+ *
+ * :type`ngtcp2_recv_crypto_data` is invoked when crypto data is
+ * received. The received data is pointed to by |data|, and its
+ * length is |datalen|. The |offset| specifies the offset where
+ * |data| is positioned. |user_data| is the arbitrary pointer passed
+ * to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. The
+ * ngtcp2 library ensures that the crypto data is passed to the
+ * application in the increasing order of |offset|. |datalen| is
+ * always strictly greater than 0. |crypto_level| indicates the
+ * encryption level where this data is received. Crypto data can
+ * never be received in
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`.
+ *
+ * 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
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_handshake_completed` is invoked when QUIC
+ * cryptographic handshake has completed.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_handshake_completed)(ngtcp2_conn *conn, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_handshake_confirmed` is invoked when QUIC
+ * cryptographic handshake is confirmed. The handshake confirmation
+ * means that both endpoints agree that handshake has finished.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_handshake_confirmed)(ngtcp2_conn *conn, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_version_negotiation` is invoked when Version
+ * Negotiation packet is received. |hd| is the pointer to the QUIC
+ * packet header object. The vector |sv| of |nsv| elements contains
+ * the QUIC version the server supports. Since Version Negotiation is
+ * only sent by server, this callback function is used by client only.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
+ */
+typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received.
+ * This callback is client 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()`.
+ *
+ * 0-RTT data accepted by the ngtcp2 library will be retransmitted by
+ * the library automatically.
+ *
+ * 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_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_encrypt` is invoked when the ngtcp2 library asks the
+ * application to encrypt packet payload. The packet payload to
+ * 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|.
+ *
+ * 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.
+ *
+ * |dest| and |plaintext| may point to the same buffer.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
+ */
+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);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_decrypt` is invoked when the ngtcp2 library asks the
+ * application to decrypt packet payload. The packet payload to
+ * 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|.
+ *
+ * The implementation of this callback must decrypt |ciphertext| using
+ * the negotiated cipher suite and write the ciphertext into the
+ * buffer pointed by |dest|. |dest| has enough capacity to store the
+ * cleartext.
+ *
+ * |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
+ * 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);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the
+ * application to produce 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|.
+ *
+ * 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 callback function must return 0 if it succeeds, or
+ * :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,
+ const uint8_t *sample);
+
+/**
+ * @macrosection
+ *
+ * Stream data flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00
+
+/**
+ * @macro
+ *
+ * :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
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` indicates that this chunk of
+ * 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
+
+/**
+ * @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| &
+ * :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.
+ *
+ * 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
+ * and a handshake has not completed yet.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *data, size_t datalen,
+ void *user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_stream_open` is a callback function which is called
+ * when remote stream is opened by peer. This function is not called
+ * if stream is opened by implicitly (we might reconsider this
+ * behaviour).
+ *
+ * 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_open)(ngtcp2_conn *conn, int64_t stream_id,
+ void *user_data);
+
+/**
+ * @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.
+ *
+ * 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);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_stream_reset` is invoked when a stream identified by
+ * |stream_id| is reset by a remote endpoint.
+ *
+ * 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_reset)(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_acked_stream_data_offset` is a callback function
+ * 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|.
+ *
+ * If a stream is closed prematurely and stream data is still
+ * in-flight, this callback function is not called for those data.
+ *
+ * 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_stream_data_offset)(
+ ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen,
+ void *user_data, void *stream_user_data);
+
+/**
+ * @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|.
+ *
+ * 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_recv_stateless_reset)(ngtcp2_conn *conn,
+ const ngtcp2_pkt_stateless_reset *sr,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_extend_max_streams` is a callback function which is
+ * called every time max stream ID is strictly extended.
+ * |max_streams| is the cumulative number of streams which an endpoint
+ * can open.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_extend_max_streams)(ngtcp2_conn *conn,
+ uint64_t max_streams, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_extend_max_stream_data` is a callback function which
+ * is invoked when max stream data is extended. |stream_id|
+ * identifies the stream. |max_data| is a cumulative number of bytes
+ * the endpoint can send on this 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_extend_max_stream_data)(ngtcp2_conn *conn,
+ int64_t stream_id,
+ uint64_t max_data, void *user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :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.
+ */
+typedef int (*ngtcp2_rand)(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx,
+ ngtcp2_rand_usage usage);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_get_new_connection_id` is a callback function to ask
+ * an application for new connection ID. Application must generate
+ * new unused connection ID with the exact |cidlen| bytes and store it
+ * in |cid|. It also has to generate stateless reset token into
+ * |token|. The length of stateless reset token is
+ * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` and it is guaranteed that
+ * the buffer pointed by |cid| has the sufficient space to store the
+ * token.
+ *
+ * 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_new_connection_id)(ngtcp2_conn *conn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_remove_connection_id` is a callback function which
+ * notifies the application that connection ID |cid| is no longer used
+ * by remote endpoint.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_remove_connection_id)(ngtcp2_conn *conn,
+ const ngtcp2_cid *cid,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_update_key` is a callback function which tells the
+ * application that it must generate new packet protection keying
+ * materials and AEAD cipher context objects with new keys. The
+ * current set of secrets are given as |current_rx_secret| and
+ * |current_tx_secret| of length |secretlen|. They are decryption and
+ * encryption secrets respectively.
+ *
+ * The application has to generate new secrets and keys for both
+ * encryption and decryption, and write decryption secret and IV to
+ * the buffer pointed by |rx_secret| and |rx_iv| respectively. It
+ * also has to create new AEAD cipher context object with new
+ * decryption key and initialize |rx_aead_ctx| with it. Similarly,
+ * write encryption secret and IV to the buffer pointed by |tx_secret|
+ * and |tx_iv|. Create new AEAD cipher context object with new
+ * encryption key and initialize |tx_aead_ctx| with it. All given
+ * buffers have the enough capacity to store secret, key and IV.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_update_key)(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data);
+
+/**
+ * @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
+ * :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`,
+ * the path validation failed.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ ngtcp2_path_validation_result res,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :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.
+ *
+ * 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,
+ const ngtcp2_preferred_addr *paddr,
+ void *user_data);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_connection_id_status_type` defines a set of status
+ * for Destination Connection ID.
+ */
+typedef enum ngtcp2_connection_id_status_type {
+ /**
+ * :enum:`NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE` indicates that
+ * a local endpoint starts using new destination Connection ID.
+ */
+ NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE,
+ /**
+ * :enum:`NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE` indicates
+ * that a local endpoint stops using a given destination Connection
+ * ID.
+ */
+ NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE
+} ngtcp2_connection_id_status_type;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_connection_id_status` is a callback function which is
+ * called when the status of Connection ID changes.
+ *
+ * |token| is the associated stateless reset token and it is ``NULL``
+ * if no token is present.
+ *
+ * |type| is the one of the value defined in
+ * :type:`ngtcp2_connection_id_status_type`. The new value might be
+ * added in the future release.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_connection_id_status)(ngtcp2_conn *conn, int type,
+ uint64_t seq, const ngtcp2_cid *cid,
+ const uint8_t *token,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_new_token` is a callback function which is
+ * called when new token is received from server.
+ *
+ * |token| is the received token.
+ *
+ * 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_new_token)(ngtcp2_conn *conn, const ngtcp2_vec *token,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which
+ * must delete the native object pointed by |aead_ctx|->native_handle.
+ */
+typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function
+ * which must delete the native object pointed by
+ * |cipher_ctx|->native_handle.
+ */
+typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
+ ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data);
+
+/**
+ * @macrosection
+ *
+ * Datagram flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_DATAGRAM_FLAG_NONE 0x00
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` indicates that DATAGRAM frame
+ * 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
+
+/**
+ * @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`.
+ *
+ * If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it
+ * indicates that DATAGRAM frame was received in 0RTT packet and a
+ * handshake has not completed yet.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_datagram)(ngtcp2_conn *conn, uint32_t flags,
+ const uint8_t *data, size_t datalen,
+ void *user_data);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_callbacks` holds a set of callback functions.
+ */
+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.
+ */
+ 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.
+ */
+ ngtcp2_recv_client_initial recv_client_initial;
+ /**
+ * :member:`recv_crypto_data` is a callback function which is
+ * invoked when cryptographic data (CRYPTO frame, in other words,
+ * TLS message) is received. This callback function must be
+ * specified.
+ */
+ ngtcp2_recv_crypto_data recv_crypto_data;
+ /**
+ * :member:`handshake_completed` is a callback function which is
+ * invoked when QUIC cryptographic handshake has completed. This
+ * callback function is optional.
+ */
+ ngtcp2_handshake_completed handshake_completed;
+ /**
+ * :member:`recv_version_negotiation` is a callback function which
+ * is invoked when Version Negotiation packet is received by a
+ * client. This callback function is optional.
+ */
+ ngtcp2_recv_version_negotiation recv_version_negotiation;
+ /**
+ * :member:`encrypt` is a callback function which is invoked to
+ * encrypt a QUIC packet. This callback function must be specified.
+ */
+ ngtcp2_encrypt encrypt;
+ /**
+ * :member:`decrypt` is a callback function which is invoked to
+ * decrypt a QUIC packet. This callback function must be specified.
+ */
+ ngtcp2_decrypt decrypt;
+ /**
+ * :member:`hp_mask` is a callback function which is invoked to get
+ * a mask to encrypt or decrypt packet header. This callback
+ * function must be specified.
+ */
+ ngtcp2_hp_mask hp_mask;
+ /**
+ * :member:`recv_stream_data` is a callback function which is
+ * invoked when STREAM data, which includes application data, is
+ * received. This callback function is optional.
+ */
+ 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
+ * largest offset of acknowledged STREAM data without a gap so that
+ * application can free memory for the data. This callback function
+ * is optional.
+ */
+ ngtcp2_acked_stream_data_offset acked_stream_data_offset;
+ /**
+ * :member:`stream_open` is a callback function which is invoked
+ * when new remote stream is opened by a remote endpoint. This
+ * callback function is optional.
+ */
+ ngtcp2_stream_open stream_open;
+ /**
+ * :member:`stream_close` is a callback function which is invoked
+ * when a stream is closed. This callback function is optional.
+ */
+ ngtcp2_stream_close stream_close;
+ /**
+ * :member:`recv_stateless_reset` is a callback function which is
+ * invoked when Stateless Reset packet is received. This callback
+ * function is optional.
+ */
+ ngtcp2_recv_stateless_reset recv_stateless_reset;
+ /**
+ * :member:`recv_retry` is a callback function which is invoked when
+ * a client receives Retry packet. For client, this callback
+ * function must be specified. Server never receive Retry packet.
+ */
+ ngtcp2_recv_retry recv_retry;
+ /**
+ * :member:`extend_max_local_streams_bidi` is a callback function
+ * which is invoked when the number of bidirectional stream which a
+ * local endpoint can open is increased. This callback function is
+ * optional.
+ */
+ ngtcp2_extend_max_streams extend_max_local_streams_bidi;
+ /**
+ * :member:`extend_max_local_streams_uni` is a callback function
+ * which is invoked when the number of unidirectional stream which a
+ * local endpoint can open is increased. This callback function is
+ * optional.
+ */
+ 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.
+ */
+ ngtcp2_rand rand;
+ /**
+ * :member:`get_new_connection_id` is a callback function which is
+ * invoked when the library needs new connection ID. This callback
+ * function must be specified.
+ */
+ ngtcp2_get_new_connection_id get_new_connection_id;
+ /**
+ * :member:`remove_connection_id` is a callback function which
+ * notifies an application that connection ID is no longer used by a
+ * remote endpoint. This callback function is optional.
+ */
+ ngtcp2_remove_connection_id remove_connection_id;
+ /**
+ * :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.
+ */
+ ngtcp2_update_key update_key;
+ /**
+ * :member:`path_validation` is a callback function which is invoked
+ * when path validation completed. This 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.
+ */
+ ngtcp2_select_preferred_addr select_preferred_addr;
+ /**
+ * :member:`stream_reset` is a callback function which is invoked
+ * when a stream is reset by a remote endpoint. This callback
+ * function is optional.
+ */
+ ngtcp2_stream_reset stream_reset;
+ /**
+ * :member:`extend_max_remote_streams_bidi` is a callback function
+ * which is invoked when the number of bidirectional streams which a
+ * remote endpoint can open is increased. This callback function is
+ * optional.
+ */
+ ngtcp2_extend_max_streams extend_max_remote_streams_bidi;
+ /**
+ * :member:`extend_max_remote_streams_uni` is a callback function
+ * which is invoked when the number of unidirectional streams which
+ * a remote endpoint can open is increased. This callback function
+ * is optional.
+ */
+ ngtcp2_extend_max_streams extend_max_remote_streams_uni;
+ /**
+ * :member:`extend_max_stream_data` is callback function which is
+ * invoked when the maximum offset of STREAM data that a local
+ * endpoint can send is increased. This callback function is
+ * optional.
+ */
+ ngtcp2_extend_max_stream_data extend_max_stream_data;
+ /**
+ * :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.
+ */
+ 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.
+ */
+ 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.
+ */
+ ngtcp2_recv_new_token recv_new_token;
+ /**
+ * :member:`delete_crypto_aead_ctx` is a callback function which
+ * deletes a given AEAD cipher context object.
+ */
+ 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.
+ */
+ ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx;
+ /**
+ * :member:`recv_datagram` is a callback function which is invoked
+ * when DATAGRAM frame is received.
+ */
+ ngtcp2_recv_datagram recv_datagram;
+} 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.
+ *
+ * The primary use case of this function is for server to send
+ * CONNECTION_CLOSE frame in Initial packet to close connection
+ * without committing the state when validating Retry token fails.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * Callback function failed.
+ */
+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_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.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * Callback function failed.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token,
+ size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_accept` is used by server implementation, and decides
+ * whether packet |pkt| of length |pktlen| is acceptable for initial
+ * packet from client.
+ *
+ * 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.
+ *
+ * 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|.
+ */
+NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen);
+
+/**
+ * @function
+ *
+ * `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.
+ *
+ * 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_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);
+
+/**
+ * @function
+ *
+ * `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 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_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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_del` frees resources allocated for |conn|. It also
+ * frees memory pointed by |conn|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of
+ * length |pktlen| and processes it. |path| is the network path the
+ * packet is delivered and must not be ``NULL``. |pi| is packet
+ * metadata and must not 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
+ * :macro:`NGTCP2_ERR_DROP_CONN` is returned, server application must
+ * drop the connection silently (without sending any CONNECTION_CLOSE
+ * frame) and discard connection 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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_pkt` is equivalent to calling
+ * `ngtcp2_conn_writev_stream` without specifying 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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC
+ * handshake has completed.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake
+ * has completed.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `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.
+ * 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.
+ *
+ * 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.
+ *
+ * After receiving Retry packet, the DCID most likely changes. In
+ * that case, client application must generate these keying materials
+ * again based on new DCID and install them again.
+ *
+ * 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_initial_key(
+ ngtcp2_conn *conn, 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.
+ *
+ * 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
+ * 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_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);
+
+/**
+ * @function
+ *
+ * `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.
+ *
+ * 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
+ * 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_tx_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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_early_key` installs packet protection AEAD
+ * cipher context object |aead_ctx|, IV |iv| of length |ivlen|, and
+ * packet header protection cipher context object |hp_ctx| to encrypt
+ * (for client) or decrypt (for server) 0RTT packets.
+ *
+ * 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
+ * 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_early_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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_rx_key` installs packet protection keying
+ * materials for decrypting Short 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.
+ *
+ * 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
+ * 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_rx_key(
+ ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_tx_key` installs packet protection keying
+ * materials for encrypting Short 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.
+ *
+ * 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
+ * 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_tx_key(
+ ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_initiate_key_update` initiates the key update.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The previous key update has not been confirmed yet; or key
+ * update is too frequent; or new keys are not available yet.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `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
+ * 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
+ * can set the error code (e.g.,
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`) using this function.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_tls_error` returns the value set by
+ * `ngtcp2_conn_set_tls_error`. If no value is set, this function
+ * returns 0.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_expiry` returns the next expiry time.
+ *
+ * Call `ngtcp2_conn_handle_expiry()` and `ngtcp2_conn_write_pkt` (or
+ * `ngtcp2_conn_writev_stream`) if expiry time is passed.
+ */
+NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_handle_expiry` handles expired timer. It does nothing
+ * if timer is not expired.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_remote_transport_params` sets transport parameter
+ * |params| 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.
+ */
+NGTCP2_EXTERN int
+ngtcp2_conn_set_remote_transport_params(ngtcp2_conn *conn,
+ const ngtcp2_transport_params *params);
+
+/**
+ * @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_EXTERN void
+ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn,
+ ngtcp2_transport_params *params);
+
+/**
+ * @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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_local_transport_params` sets the local transport
+ * parameters |params|. This function can only be called by server.
+ * Although the local transport parameters are passed to
+ * `ngtcp2_conn_server_new`, server might want to update them after
+ * ALPN is chosen. In that case, server can update the transport
+ * parameter with this function. Server must call this function
+ * before calling `ngtcp2_conn_install_tx_handshake_key`.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_local_transport_params` fills settings values in
+ * |params|.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn,
+ ngtcp2_transport_params *params);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_open_bidi_stream` opens new bidirectional stream. The
+ * |stream_user_data| is the user data specific to the stream. The
+ * open stream ID is stored in |*pstream_id|.
+ *
+ * Application can call this function before handshake completes. For
+ * 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_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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED`
+ * The remote peer does not allow |stream_id| yet.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn,
+ int64_t *pstream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_open_uni_stream` opens new unidirectional stream. The
+ * |stream_user_data| is the user data specific to the stream. The
+ * open stream ID is stored in |*pstream_id|.
+ *
+ * Application can call this function before handshake completes. For
+ * 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_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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED`
+ * The remote peer does not allow |stream_id| yet.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn,
+ int64_t *pstream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_shutdown_stream` closes stream denoted by |stream_id|
+ * abruptly. |app_error_code| is one of application error codes, and
+ * indicates the reason of shutdown. Successful call of this function
+ * does not immediately erase the state of the stream. The actual
+ * deletion is done when the remote endpoint sends acknowledgement.
+ * Calling this function is equivalent to call
+ * `ngtcp2_conn_shutdown_stream_read`, and
+ * `ngtcp2_conn_shutdown_stream_write` sequentially.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :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,
+ uint64_t app_error_code);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_shutdown_stream_write` closes write-side of stream
+ * denoted by |stream_id| abruptly. |app_error_code| is one of
+ * application error codes, and indicates the reason of shutdown. If
+ * this function succeeds, no application data is sent to the remote
+ * endpoint. It discards all data which has not been acknowledged
+ * yet.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :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,
+ uint64_t app_error_code);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_shutdown_stream_read` closes read-side of stream
+ * denoted by |stream_id| abruptly. |app_error_code| is one of
+ * application error codes, and indicates the reason of shutdown. If
+ * this function succeeds, no application data is forwarded to an
+ * application layer.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :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,
+ uint64_t app_error_code);
+
+/**
+ * @macrosection
+ *
+ * Write stream data flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00
+
+/**
+ * @macro
+ *
+ * :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
+
+/**
+ * @macro
+ *
+ * :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
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_stream` is just like
+ * `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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_writev_stream` writes a packet containing stream data
+ * of stream denoted by |stream_id|. The buffer of the packet is
+ * pointed by |dest| of length |destlen|. This function performs QUIC
+ * handshake as well.
+ *
+ * Specifying -1 to |stream_id| means no new stream data to send.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the 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. When calling
+ * this function again after it returns
+ * :macro:`NGTCP2_ERR_WRITE_MORE`, caller must pass the same |pi| to
+ * this function.
+ *
+ * If the all given data is encoded as STREAM frame in |dest|, and if
+ * |flags| & :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is nonzero, fin
+ * flag is set to outgoing STREAM frame. Otherwise, fin flag in
+ * STREAM frame is not set.
+ *
+ * This packet may contain frames other than STREAM frame. The packet
+ * might not contain STREAM frame if other frames occupy the packet.
+ * In that case, |*pdatalen| would be -1 if |pdatalen| is not
+ * ``NULL``.
+ *
+ * If |flags| & :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is nonzero, and
+ * 0 length STREAM frame is successfully serialized, |*pdatalen| would
+ * be 0.
+ *
+ * The number of data encoded in STREAM frame is stored in |*pdatalen|
+ * if it is not ``NULL``. The caller must keep the portion of data
+ * covered by |*pdatalen| bytes in tact until
+ * :type:`ngtcp2_acked_stream_data_offset` indicates that they are
+ * acknowledged by a remote endpoint or the stream is closed.
+ *
+ * If |flags| equals to :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE`, this
+ * function produces a single payload of UDP packet. If the given
+ * stream data is small (e.g., few bytes), the packet might be
+ * severely under filled. Too many small packet might increase
+ * overall packet processing costs. Unless there are retransmissions,
+ * by default, application can only send 1 STREAM frame in one QUIC
+ * packet. In order to include more than 1 STREAM frame in one QUIC
+ * packet, specify :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
+ * outcomes:
+ *
+ * - The function returns the written length of packet just like
+ * without :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`. This is because
+ * packet is nearly full and the library decided to make a complete
+ * packet. |*pdatalen| might be -1 or >= 0.
+ *
+ * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`. In this
+ * case, |*pdatalen| >= 0 is asserted. This indicates that
+ * application can call this function with different stream data (or
+ * `ngtcp2_conn_writev_datagram` if it has data to send in
+ * unreliable datagram) to pack them into the same packet.
+ * Application has to specify the same |conn|, |path|, |pi|, |dest|,
+ * |destlen|, and |ts| parameters, otherwise the behaviour is
+ * undefined. The application can change |flags|.
+ *
+ * - The function returns :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` which
+ * indicates that stream is blocked because of flow control.
+ *
+ * - The other error might be returned just like without
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`.
+ *
+ * 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
+ * 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).
+ *
+ * This function returns 0 if it cannot write any frame because buffer
+ * is too small, or packet is congestion limited. Application should
+ * keep reading and wait for congestion window to grow.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
+ * Stream does not exist
+ * :macro:`NGTCP2_ERR_STREAM_SHUT_WR`
+ * Stream is half closed (local); or stream is being reset.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`
+ * Stream is blocked because of flow control.
+ * :macro:`NGTCP2_ERR_WRITE_MORE`
+ * (Only when :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is specified)
+ * Application can call this function to pack more stream data
+ * into the same packet. See above to know how it works.
+ *
+ * In general, if the error code which satisfies
+ * `ngtcp2_err_is_fatal(err) <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.
+ */
+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);
+
+/**
+ * @macrosection
+ *
+ * Write datagram flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00
+
+/**
+ * @macro
+ *
+ * :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
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_writev_datagram` writes a packet containing unreliable
+ * data in DATAGRAM frame. The buffer of the packet is pointed by
+ * |dest| of length |destlen|. This function performs QUIC handshake
+ * as well.
+ *
+ * For |path| and |pi| parameters, refer to
+ * `ngtcp2_conn_writev_stream`.
+ *
+ * If the given data is written to the buffer, nonzero value is
+ * assigned to |*paccepted| if it is not NULL. The data in DATAGRAM
+ * frame cannot be fragmented; writing partial data is not possible.
+ *
+ * This function might write other frames other than DATAGRAM, just
+ * like `ngtcp2_conn_writev_stream`.
+ *
+ * If the function returns 0, it means that no more data cannot be
+ * sent because of congestion control limit; or, data does not fit
+ * into the provided buffer; or, a local endpoint, as a server, is
+ * unable to send data because of its amplification limit. In this
+ * case, |*paccepted| is assigned zero if it is not NULL.
+ *
+ * If :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is set in |flags|,
+ * there are 3 outcomes:
+ *
+ * - The function returns the written length of packet just like
+ * without :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`. This is
+ * because packet is nearly full and the library decided to make a
+ * complete packet. |*paccepted| might be zero or nonzero.
+ *
+ * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`. In this
+ * case, |*paccepted| != 0 is asserted. This indicates that
+ * application can call this function with another unreliable data
+ * (or `ngtcp2_conn_writev_stream` if it has stream data to send) to
+ * pack them into the same packet. Application has to specify the
+ * same |conn|, |path|, |pi|, |dest|, |destlen|, and |ts|
+ * parameters, otherwise the behaviour is undefined. The
+ * application can change |flags|.
+ *
+ * - The other error might be returned just like without
+ * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`.
+ *
+ * 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
+ * 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
+ * ready).
+ *
+ * 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_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ * :macro:`NGTCP2_ERR_WRITE_MORE`
+ * (Only when :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is
+ * specified) Application can call this function to pack more data
+ * into the same packet. See above to know how it works.
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * A remote endpoint did not express the DATAGRAM frame support.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The provisional DATAGRAM frame size exceeds the maximum
+ * DATAGRAM frame size that a remote endpoint can receive.
+ *
+ * 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.
+ */
+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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in
+ * closing period.
+ */
+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.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_stream_offset` extends stream's max stream
+ * data value by |datalen|.
+ *
+ * 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
+ */
+NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn,
+ int64_t stream_id,
+ uint64_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_offset` extends max data offset by
+ * |datalen|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn,
+ uint64_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_streams_bidi` extends the number of maximum
+ * local bidirectional streams that a remote endpoint can open by |n|.
+ *
+ * The library does not increase maximum stream limit automatically.
+ * The exception is when a stream is closed without
+ * :type:`ngtcp2_stream_open` callback being called. In this case,
+ * stream limit is increased automatically.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn,
+ size_t n);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_streams_uni` extends the number of maximum
+ * local unidirectional streams that a remote endpoint can open by
+ * |n|.
+ *
+ * The library does not increase maximum stream limit automatically.
+ * The exception is when a stream is closed without
+ * :type:`ngtcp2_stream_open` callback being called. In this case,
+ * stream limit is increased automatically.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn,
+ size_t n);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_dcid` returns the non-NULL pointer to destination
+ * connection ID. If no destination connection ID is present, the
+ * return value is not ``NULL``, and its datalen field is 0.
+ */
+NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_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.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_scid` writes the all source connection IDs which
+ * the local endpoint has provided to the peer and have not retired in
+ * |dest|. The buffer pointed by |dest| must have
+ * ``sizeof(ngtcp2_cid) * n`` bytes available, where n is the return
+ * value of `ngtcp2_conn_get_num_scid()`.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_num_active_dcid` returns the number of the active
+ * destination connection ID.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cid_token` is the convenient struct to store
+ * Connection ID, its associated path, and stateless reset token.
+ */
+typedef struct ngtcp2_cid_token {
+ /**
+ * :member:`seq` is the sequence number of this Connection ID.
+ */
+ uint64_t seq;
+ /**
+ * :member:`cid` is Connection ID.
+ */
+ ngtcp2_cid cid;
+ /**
+ * :member:`ps` is the path which is associated to this Connection
+ * ID.
+ */
+ ngtcp2_path_storage ps;
+ /**
+ * :member:`token` is the stateless reset token for this Connection
+ * ID.
+ */
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ /**
+ * :member:`token_present` is nonzero if token contains stateless
+ * reset token.
+ */
+ uint8_t token_present;
+} ngtcp2_cid_token;
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_active_dcid` writes the all active destination
+ * connection IDs and tokens to |dest|. The buffer pointed by |dest|
+ * must have ``sizeof(ngtcp2_cid_token) * n`` bytes available, where n
+ * is the return value of `ngtcp2_conn_get_num_active_dcid()`.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn,
+ ngtcp2_cid_token *dest);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_negotiated_version` returns the negotiated version.
+ */
+NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_early_data_rejected` tells |conn| that 0-RTT data was
+ * rejected by a server.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to
+ * |*cstat|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn,
+ ngtcp2_conn_stat *cstat);
+
+/**
+ * @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_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_submit_crypto_data` submits crypto stream data |data|
+ * 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.
+ */
+NGTCP2_EXTERN int
+ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, const size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_submit_new_token` submits address validation token.
+ * It is sent in NEW_TOKEN frame. Only server can call this function.
+ * |tokenlen| must not be 0.
+ *
+ * This function makes a copy of the buffer pointed by |token| of
+ * length |tokenlen|.
+ *
+ * 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_submit_new_token(ngtcp2_conn *conn,
+ const uint8_t *token,
+ size_t tokenlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to
+ * |conn|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn,
+ const ngtcp2_addr *addr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_remote_addr` sets remote endpoint address |addr|
+ * to |conn|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn,
+ const ngtcp2_addr *addr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_path` returns the current path.
+ */
+NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn);
+
+/**
+ * @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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * Migration is disabled.
+ * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED`
+ * No unused connection ID is available.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |path| equals the current path.
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ */
+NGTCP2_EXTERN int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_max_local_streams_uni` returns the cumulative
+ * number of streams which local endpoint can open.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_max_data_left` returns the number of bytes that
+ * this local endpoint can send in this connection.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_streams_bidi_left` returns the number of
+ * bidirectional streams which the local endpoint can open without
+ * violating stream concurrency limit.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_streams_uni_left` returns the number of
+ * unidirectional streams which the local endpoint can open without
+ * violating stream concurrency limit.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_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
+ * :type:`ngtcp2_hp_mask` callbacks.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_initial_crypto_ctx` returns
+ * :type:`ngtcp2_crypto_ctx` object for Initial packet encryption.
+ */
+NGTCP2_EXTERN const ngtcp2_crypto_ctx *
+ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/Short packet
+ * encryption. The passed data will be passed to
+ * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
+ * :type:`ngtcp2_hp_mask` callbacks.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_tls_native_handle` returns TLS native handle set by
+ * `ngtcp2_conn_set_tls_native_handle()`.
+ */
+NGTCP2_EXTERN void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_tls_native_handle` sets TLS native handle
+ * |tls_native_handle| to |conn|. Internally, it is used as an opaque
+ * pointer.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn,
+ void *tls_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_retry_aead` sets |aead| and |aead_ctx| for Retry
+ * integrity tag verification. |aead| must be AEAD_AES_128_GCM.
+ * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as
+ * encryption key. This function must be called if |conn| is
+ * initialized as client. Server does not verify the tag and has no
+ * need to call this function.
+ *
+ * If this function succeeds, |conn| takes ownership of |aead_ctx|.
+ * :type:`ngtcp2_delete_crypto_aead_ctx` will be called to delete this
+ * object when it is no longer used. If this function fails, the
+ * caller is responsible to delete it.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx`
+ * object for Handshake/Short packet encryption.
+ */
+NGTCP2_EXTERN const ngtcp2_crypto_ctx *
+ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_early_crypto_ctx` sets |ctx| for 0RTT packet
+ * encryption. The passed data will be passed to
+ * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
+ * :type:`ngtcp2_hp_mask` callbacks.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_set_early_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_early_crypto_ctx` returns
+ * :type:`ngtcp2_crypto_ctx` object for 0RTT packet encryption.
+ */
+NGTCP2_EXTERN const ngtcp2_crypto_ctx *
+ngtcp2_conn_get_early_crypto_ctx(ngtcp2_conn *conn);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_connection_close_error_code_type` defines connection
+ * error code type.
+ */
+typedef enum ngtcp2_connection_close_error_code_type {
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`
+ * indicates the error code is QUIC transport error code.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`
+ * indicates the error code is application error code.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION,
+} ngtcp2_connection_close_error_code_type;
+
+/**
+ * @struct
+ *
+ * `ngtcp2_connection_close_error_code` contains connection error code
+ * and its type.
+ */
+typedef struct ngtcp2_connection_close_error_code {
+ /**
+ * :member:`error_code` is the error code for connection closure.
+ */
+ uint64_t error_code;
+ /**
+ * :member:`type` is the type of :member:`error_code`.
+ */
+ ngtcp2_connection_close_error_code_type type;
+} ngtcp2_connection_close_error_code;
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_connection_close_error_code` stores the received
+ * connection close error code in |ccec|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_get_connection_close_error_code(
+ ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_is_local_stream` returns nonzero if |stream_id| denotes the
+ * stream which a local endpoint issues.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_is_server` returns nonzero if |conn| is initialized as
+ * server.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_is_server(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_after_retry` returns nonzero if |conn| as a client has
+ * received Retry packet from server and successfully validated it.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_after_retry(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_stream_user_data` sets |stream_user_data| to the
+ * stream identified by |stream_id|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
+ * Stream does not exist
+ */
+NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn,
+ int64_t stream_id,
+ void *stream_user_data);
+
+/**
+ * @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`).
+ */
+NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr);
+
+/**
+ * @function
+ *
+ * `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`).
+ */
+NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr);
+
+/**
+ * @function
+ *
+ * `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_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_addr_init` initializes |dest| with the given arguments and
+ * returns |dest|.
+ */
+NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest,
+ const struct sockaddr *addr,
+ size_t addrlen, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_path_storage_init` initializes |ps| with the given
+ * 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);
+
+/**
+ * @function
+ *
+ * `ngtcp2_path_storage_zero` initializes |ps| with the zero length
+ * addresses.
+ */
+NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps);
+
+/**
+ * @function
+ *
+ * `ngtcp2_settings_default` initializes |settings| with the default
+ * values. First this function fills |settings| with 0 and set the
+ * default value to the following fields:
+ *
+ * * :type:`cc_algo <ngtcp2_settings.cc_algo>` =
+ * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC`
+ * * :type:`initial_rtt <ngtcp2_settings.initial_rtt>` =
+ * :macro:`NGTCP2_DEFAULT_INITIAL_RTT`
+ * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
+ */
+NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings);
+
+/**
+ * @function
+ *
+ * `ngtcp2_transport_params_default` initializes |params| with the
+ * default values. First this function fills |params| with 0 and set
+ * the default value to the following fields:
+ *
+ * * :type:`max_udp_payload_size
+ * <ngtcp2_transport_params.max_udp_payload_size>` =
+ * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE`
+ * * :type:`ack_delay_exponent
+ * <ngtcp2_transport_params.ack_delay_exponent>` =
+ * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT`
+ * * :type:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` =
+ * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY`
+ * * :type:`active_connection_id_limit
+ * <ngtcp2_transport_params.active_connection_id_limit>` =
+ * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT`
+ */
+NGTCP2_EXTERN void
+ngtcp2_transport_params_default(ngtcp2_transport_params *params);
+
+/**
+ * @function
+ *
+ * `ngtcp2_mem_default` returns the default, system standard memory
+ * allocator.
+ */
+NGTCP2_EXTERN const ngtcp2_mem *ngtcp2_mem_default(void);
+
+/**
+ * @macrosection
+ *
+ * ngtcp2_info macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_VERSION_AGE` is the age of :type:`ngtcp2_info`
+ */
+#define NGTCP2_VERSION_AGE 1
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_info` is what `ngtcp2_version()` returns. It holds
+ * information about the particular ngtcp2 version.
+ */
+typedef struct ngtcp2_info {
+ /**
+ * :member:`age` is the age of this struct. This instance of ngtcp2
+ * sets it to :macro:`NGTCP2_VERSION_AGE` but a future version may
+ * bump it and add more struct fields at the bottom
+ */
+ int age;
+ /**
+ * :member:`version_num` is the :macro:`NGTCP2_VERSION_NUM` number
+ * (since age ==1)
+ */
+ int version_num;
+ /**
+ * :member:`version_str` points to the :macro:`NGTCP2_VERSION`
+ * string (since age ==1)
+ */
+ const char *version_str;
+ /* -------- the above fields all exist when age == 1 */
+} 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_EXTERN ngtcp2_info *ngtcp2_version(int least_version);
+
+/**
+ * @function
+ *
+ * `ngtcp2_is_bidi_stream` returns nonzero if |stream_id| denotes
+ * bidirectional stream.
+ */
+NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_log_event` defines an event of ngtcp2 library
+ * internal logger.
+ */
+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;
+
+/**
+ * @function
+ *
+ * `ngtcp2_log_info` writes info level log.
+ */
+NGTCP2_EXTERN void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev,
+ const char *fmt, ...);
+
+/**
+ * @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_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src);
+
+/**
+ * @function
+ *
+ * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same
+ * local and remote addresses.
+ */
+NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h
new file mode 100644
index 0000000000..fb0671de9f
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h
@@ -0,0 +1,51 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2016 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 VERSION_H
+#define VERSION_H
+
+/**
+ * @macrosection
+ *
+ * Library version macros
+ */
+
+/**
+ * @macro
+ *
+ * Version number of the ngtcp2 library release.
+ */
+#define NGTCP2_VERSION "0.1.0-DEV"
+
+/**
+ * @macro
+ *
+ * Numerical representation of the version number of the ngtcp2
+ * library release. This is a 24 bit number with 8 bits for major
+ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3
+ * becomes 0x010203.
+ */
+#define NGTCP2_VERSION_NUM 0x000100
+
+#endif /* VERSION_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c
new file mode 100644
index 0000000000..7a7f3e469a
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c
@@ -0,0 +1,318 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_acktr.h"
+
+#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));
+ if (*ent == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ (*ent)->pkt_num = pkt_num;
+ (*ent)->len = 1;
+ (*ent)->tstamp = tstamp;
+
+ return 0;
+}
+
+void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, ent);
+}
+
+static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs > *(int64_t *)rhs;
+}
+
+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),
+ 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;
+ }
+
+ acktr->log = log;
+ acktr->mem = mem;
+ acktr->flags = NGTCP2_ACKTR_FLAG_NONE;
+ acktr->first_unacked_ts = UINT64_MAX;
+ acktr->rx_npkt = 0;
+
+ return 0;
+}
+
+void ngtcp2_acktr_free(ngtcp2_acktr *acktr) {
+ ngtcp2_ksl_it it;
+
+ if (acktr == NULL) {
+ return;
+ }
+
+ 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_ksl_free(&acktr->ents);
+
+ ngtcp2_ringbuf_free(&acktr->acks);
+}
+
+int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ksl_it it;
+ ngtcp2_acktr_entry *ent, *prev_ent, *delent;
+ int rv;
+ int added = 0;
+
+ if (ngtcp2_ksl_len(&acktr->ents)) {
+ it = ngtcp2_ksl_lower_bound(&acktr->ents, &pkt_num);
+ if (ngtcp2_ksl_it_end(&it)) {
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ assert(ent->pkt_num >= pkt_num + (int64_t)ent->len);
+
+ if (ent->pkt_num == pkt_num + (int64_t)ent->len) {
+ ++ent->len;
+ added = 1;
+ }
+ } else {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ assert(ent->pkt_num != pkt_num);
+
+ if (ngtcp2_ksl_it_begin(&it)) {
+ if (ent->pkt_num + 1 == pkt_num) {
+ ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num);
+ ent->pkt_num = pkt_num;
+ ent->tstamp = ts;
+ ++ent->len;
+ added = 1;
+ }
+ } else {
+ ngtcp2_ksl_it_prev(&it);
+ prev_ent = ngtcp2_ksl_it_get(&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);
+ added = 1;
+ } else {
+ ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num);
+ ent->pkt_num = pkt_num;
+ ent->tstamp = ts;
+ ++ent->len;
+ added = 1;
+ }
+ } else if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) {
+ ++prev_ent->len;
+ added = 1;
+ }
+ }
+ }
+ }
+
+ if (!added) {
+ rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem);
+ 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);
+ return rv;
+ }
+ }
+
+ if (active_ack) {
+ acktr->flags |= NGTCP2_ACKTR_FLAG_ACTIVE_ACK;
+ if (acktr->first_unacked_ts == UINT64_MAX) {
+ acktr->first_unacked_ts = ts;
+ }
+ }
+
+ if (ngtcp2_ksl_len(&acktr->ents) > NGTCP2_ACKTR_MAX_ENT) {
+ 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);
+ }
+
+ return 0;
+}
+
+void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) {
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_lower_bound(&acktr->ents, &ent->pkt_num);
+ assert(*(int64_t *)ngtcp2_ksl_it_key(&it) == (int64_t)ent->pkt_num);
+
+ 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_it ngtcp2_acktr_get(ngtcp2_acktr *acktr) {
+ return ngtcp2_ksl_begin(&acktr->ents);
+}
+
+int ngtcp2_acktr_empty(ngtcp2_acktr *acktr) {
+ ngtcp2_ksl_it it = ngtcp2_ksl_begin(&acktr->ents);
+ return ngtcp2_ksl_it_end(&it);
+}
+
+ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
+ int64_t pkt_num,
+ int64_t largest_ack) {
+ ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks);
+
+ ent->largest_ack = largest_ack;
+ ent->pkt_num = pkt_num;
+
+ return ent;
+}
+
+/*
+ * acktr_remove removes |ent| from |acktr|. 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);
+}
+
+static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
+ size_t ack_ent_offset) {
+ ngtcp2_acktr_ack_entry *ack_ent;
+ ngtcp2_acktr_entry *ent;
+ ngtcp2_ksl_it it;
+
+ assert(ngtcp2_ringbuf_len(rb));
+
+ ack_ent = ngtcp2_ringbuf_get(rb, ack_ent_offset);
+
+ /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
+ it = ngtcp2_ksl_lower_bound(&acktr->ents, &ack_ent->largest_ack);
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+ acktr_remove(acktr, &it, ent);
+ }
+
+ if (ngtcp2_ksl_len(&acktr->ents)) {
+ assert(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+ if (ent->pkt_num > ack_ent->largest_ack &&
+ ack_ent->largest_ack >= ent->pkt_num - (int64_t)(ent->len - 1)) {
+ ent->len = (size_t)(ent->pkt_num - ack_ent->largest_ack);
+ }
+ }
+
+ ngtcp2_ringbuf_resize(rb, ack_ent_offset);
+}
+
+void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
+ ngtcp2_acktr_ack_entry *ent;
+ int64_t largest_ack = fr->largest_ack, min_ack;
+ size_t i, j;
+ ngtcp2_ringbuf *rb = &acktr->acks;
+ size_t nacks = ngtcp2_ringbuf_len(rb);
+
+ /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
+ for (j = 0; j < nacks; ++j) {
+ ent = ngtcp2_ringbuf_get(rb, j);
+ if (largest_ack >= ent->pkt_num) {
+ break;
+ }
+ }
+ if (j == nacks) {
+ return;
+ }
+
+ min_ack = largest_ack - (int64_t)fr->first_ack_blklen;
+
+ if (min_ack <= ent->pkt_num && ent->pkt_num <= largest_ack) {
+ acktr_on_ack(acktr, rb, j);
+ return;
+ }
+
+ for (i = 0; i < fr->num_blks && j < nacks; ++i) {
+ largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2;
+ min_ack = largest_ack - (int64_t)fr->blks[i].blklen;
+
+ for (;;) {
+ if (ent->pkt_num > largest_ack) {
+ ++j;
+ if (j == nacks) {
+ return;
+ }
+ ent = ngtcp2_ringbuf_get(rb, j);
+ continue;
+ }
+ if (ent->pkt_num < min_ack) {
+ break;
+ }
+ acktr_on_ack(acktr, rb, j);
+ return;
+ }
+ }
+}
+
+void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) {
+ acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK |
+ NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK |
+ NGTCP2_ACKTR_FLAG_CANCEL_TIMER);
+ acktr->first_unacked_ts = UINT64_MAX;
+ acktr->rx_npkt = 0;
+}
+
+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;
+}
+
+void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) {
+ acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h
new file mode 100644
index 0000000000..51e1588e3c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h
@@ -0,0 +1,212 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ACKTR_H
+#define NGTCP2_ACKTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_ringbuf.h"
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_pkt.h"
+
+/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry
+ which ngtcp2_acktr stores. */
+#define NGTCP2_ACKTR_MAX_ENT 1024
+
+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;
+} ngtcp2_acktr_entry;
+
+/*
+ * 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|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * 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);
+
+/*
+ * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It
+ * deallocates memory pointed by |ent|.
+ */
+void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem);
+
+typedef struct ngtcp2_acktr_ack_entry {
+ /* largest_ack is the largest packet number in outgoing ACK frame */
+ int64_t largest_ack;
+ /* pkt_num is the packet number that ACK frame is included. */
+ int64_t pkt_num;
+} ngtcp2_acktr_ack_entry;
+
+/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */
+#define NGTCP2_ACKTR_FLAG_NONE 0x00
+/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate
+ acknowledgement is required. */
+#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01
+/* 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
+/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
+ expired and canceled. */
+#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100
+
+/*
+ * ngtcp2_acktr tracks received packets which we have to send ack.
+ */
+typedef struct ngtcp2_acktr {
+ ngtcp2_ringbuf acks;
+ /* ents includes ngtcp2_acktr_entry sorted by decreasing order of
+ packet number. */
+ ngtcp2_ksl ents;
+ ngtcp2_log *log;
+ const ngtcp2_mem *mem;
+ /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_FLAG_*. */
+ uint16_t flags;
+ /* first_unacked_ts is timestamp when ngtcp2_acktr_entry is added
+ first time after the last outgoing ACK frame. */
+ ngtcp2_tstamp first_unacked_ts;
+ /* rx_npkt is the number of packets received without sending ACK. */
+ size_t rx_npkt;
+} ngtcp2_acktr;
+
+/*
+ * ngtcp2_acktr_init initializes |acktr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_acktr_free frees resources allocated for |acktr|. It frees
+ * any ngtcp2_acktr_entry added to |acktr|.
+ */
+void ngtcp2_acktr_free(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_add adds packet number |pkt_num| to |acktr|.
+ * |active_ack| is nonzero if |pkt_num| is retransmittable packet.
+ *
+ * This function assumes that |acktr| does not contain |pkt_num|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * OUt of memory.
+ */
+int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_acktr_forget removes all entries which have the packet
+ * number that is equal to or less than ent->pkt_num. This function
+ * assumes that |acktr| includes |ent|.
+ */
+void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent);
+
+/*
+ * ngtcp2_acktr_get returns the pointer to pointer to the entry which
+ * has the largest packet number to be acked. If there is no entry,
+ * returned value satisfies ngtcp2_ksl_it_end(&it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_empty returns nonzero if it has no packet to
+ * acknowledge.
+ */
+int ngtcp2_acktr_empty(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_add_ack records outgoing ACK frame whose largest
+ * acknowledged packet number is |largest_ack|. |pkt_num| is the
+ * packet number of a packet in which ACK frame is included. This
+ * function returns a pointer to the object it adds.
+ */
+ngtcp2_acktr_ack_entry *
+ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, int64_t pkt_num, int64_t largest_ack);
+
+/*
+ * ngtcp2_acktr_recv_ack processes the incoming ACK frame |fr|.
+ * |pkt_num| is a packet number which includes |fr|. If we receive
+ * ACK which acknowledges the ACKs added by ngtcp2_acktr_add_ack,
+ * ngtcp2_acktr_entry which the outgoing ACK acknowledges is removed.
+ */
+void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr);
+
+/*
+ * ngtcp2_acktr_commit_ack tells |acktr| that ACK frame is generated.
+ */
+void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_require_active_ack returns nonzero if ACK frame should
+ * be generated actively.
+ */
+int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr,
+ ngtcp2_duration max_ack_delay,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_acktr_immediate_ack tells |acktr| that immediate
+ * acknowledgement is required.
+ */
+void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr);
+
+#endif /* NGTCP2_ACKTR_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c
new file mode 100644
index 0000000000..22af219a9e
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c
@@ -0,0 +1,131 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_addr.h"
+
+#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) {
+ dest->addrlen = addrlen;
+ dest->addr = (struct sockaddr *)addr;
+ dest->user_data = user_data;
+ 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);
+ }
+ dest->user_data = src->user_data;
+}
+
+void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr,
+ size_t addrlen) {
+ dest->addrlen = addrlen;
+ if (addrlen) {
+ memcpy(dest->addr, addr, addrlen);
+ }
+}
+
+static int sockaddr_eq(const struct sockaddr *a, const struct 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;
+ 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;
+ return ai->sin6_port == bi->sin6_port &&
+ memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
+ }
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) {
+ return a->addr->sa_family == b->addr->sa_family &&
+ sockaddr_eq(a->addr, b->addr);
+}
+
+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;
+
+ 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;
+ if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
+ }
+ if (ai->sin_port != bi->sin_port) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT;
+ }
+ return flags;
+ }
+ case AF_INET6: {
+ const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a,
+ *bi = (const struct sockaddr_in6 *)(void *)b;
+ if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
+ }
+ if (ai->sin6_port != bi->sin6_port) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT;
+ }
+ return flags;
+ }
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_addr_empty(const ngtcp2_addr *addr) { return addr->addrlen == 0; }
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h
new file mode 100644
index 0000000000..84c0c01ddb
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h
@@ -0,0 +1,78 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ADDR_H
+#define NGTCP2_ADDR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_addr_copy copies |src| to |dest|. This function assumes
+ * that dest->addr points to a buffer which have sufficient size to
+ * store the copy.
+ */
+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
+/* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not
+ match. */
+#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1
+/* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */
+#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2
+/* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not
+ match. */
+#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4
+
+/*
+ * ngtcp2_addr_compare compares address and port between |a| and |b|,
+ * and returns zero or more of NGTCP2_ADDR_COMPARE_FLAG_*.
+ */
+uint32_t ngtcp2_addr_compare(const ngtcp2_addr *a, const ngtcp2_addr *b);
+
+/*
+ * ngtcp2_addr_empty returns nonzero if |addr| has zero length
+ * address.
+ */
+int ngtcp2_addr_empty(const ngtcp2_addr *addr);
+
+#endif /* NGTCP2_ADDR_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c
new file mode 100644
index 0000000000..373f23d91a
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c
@@ -0,0 +1,44 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_buf.h"
+
+void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
+ buf->begin = buf->pos = buf->last = begin;
+ buf->end = begin + 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_len(const ngtcp2_buf *buf) {
+ return (size_t)(buf->last - buf->pos);
+}
+
+size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) {
+ return (size_t)(buf->end - buf->begin);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h
new file mode 100644
index 0000000000..8f2c36a1bb
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h
@@ -0,0 +1,79 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BUF_H
+#define NGTCP2_BUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_buf {
+ /* begin points to the beginning of the buffer. */
+ uint8_t *begin;
+ /* end points to the one beyond of the last byte of the buffer */
+ uint8_t *end;
+ /* pos pointers to the start of data. Typically, this points to the
+ point that next data should be read. Initially, it points to
+ |begin|. */
+ uint8_t *pos;
+ /* last points to the one beyond of the last data of the buffer.
+ Typically, new data is written at this point. Initially, it
+ points to |begin|. */
+ uint8_t *last;
+} ngtcp2_buf;
+
+/*
+ * ngtcp2_buf_init initializes |buf| with the given buffer.
+ */
+void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len);
+
+/*
+ * ngtcp2_buf_reset resets pos and last fields to match begin field to
+ * make ngtcp2_buf_len(buf) return 0.
+ */
+void ngtcp2_buf_reset(ngtcp2_buf *buf);
+
+/*
+ * ngtcp2_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.
+ */
+size_t ngtcp2_buf_left(const ngtcp2_buf *buf);
+
+/*
+ * 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);
+
+/*
+ * ngtcp2_buf_cap returns the capacity of the buffer. In other words,
+ * it returns buf->end - buf->begin.
+ */
+size_t ngtcp2_buf_cap(const ngtcp2_buf *buf);
+
+#endif /* NGTCP2_BUF_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c
new file mode 100644
index 0000000000..f4670805c7
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c
@@ -0,0 +1,536 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_cc.h"
+
+#include <assert.h>
+
+#if defined(_MSC_VER)
+# include <intrin.h>
+#endif
+
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_rcvry.h"
+
+uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
+ uint64_t n = 2 * max_udp_payload_size;
+ n = ngtcp2_max(n, 14720);
+ return ngtcp2_min(10 * max_udp_payload_size, n);
+}
+
+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) {
+ pkt->pkt_num = pkt_num;
+ pkt->pktlen = pktlen;
+ pkt->pktns_id = pktns_id;
+ pkt->ts_sent = ts_sent;
+
+ return pkt;
+}
+
+static void reno_cc_reset(ngtcp2_reno_cc *cc) {
+ cc->max_delivery_rate_sec = 0;
+ cc->target_cwnd = 0;
+ cc->pending_add = 0;
+}
+
+void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log) {
+ cc->ccb.log = log;
+ reno_cc_reset(cc);
+}
+
+void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc) { (void)cc; }
+
+int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem) {
+ ngtcp2_reno_cc *reno_cc;
+
+ reno_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_reno_cc));
+ if (reno_cc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_reno_cc_init(reno_cc, log);
+
+ cc->ccb = &reno_cc->ccb;
+ cc->on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked;
+ cc->congestion_event = ngtcp2_cc_reno_cc_congestion_event;
+ cc->on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion;
+ cc->on_ack_recv = ngtcp2_cc_reno_cc_on_ack_recv;
+ cc->reset = ngtcp2_cc_reno_cc_reset;
+
+ return 0;
+}
+
+void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_reno_cc *reno_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_reno_cc, ccb);
+
+ ngtcp2_reno_cc_free(reno_cc);
+ ngtcp2_mem_free(mem, reno_cc);
+}
+
+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_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts) {
+ ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ uint64_t m;
+ (void)ts;
+
+ if (in_congestion_recovery(cstat, pkt->ts_sent)) {
+ return;
+ }
+
+ if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) {
+ return;
+ }
+
+ if (cstat->cwnd < cstat->ssthresh) {
+ cstat->cwnd += pkt->pktlen;
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
+ pkt->pkt_num, cstat->cwnd);
+ return;
+ }
+
+ m = cstat->max_udp_payload_size * pkt->pktlen + cc->pending_add;
+ cc->pending_add = m % cstat->cwnd;
+
+ cstat->cwnd += m / cstat->cwnd;
+}
+
+void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts_sent,
+ 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)) {
+ return;
+ }
+
+ cstat->congestion_recovery_start_ts = ts;
+ cstat->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS;
+ min_cwnd = 2 * cstat->max_udp_payload_size;
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, min_cwnd);
+ cstat->ssthresh = cstat->cwnd;
+
+ cc->pending_add = 0;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "reduce cwnd because of packet loss cwnd=%" PRIu64,
+ cstat->cwnd);
+}
+
+void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)ts;
+
+ cstat->cwnd = 2 * cstat->max_udp_payload_size;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+}
+
+void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ uint64_t target_cwnd, initcwnd;
+ (void)ts;
+
+ /* TODO Use sliding window for min rtt measurement */
+ /* TODO Use sliding window */
+ cc->max_delivery_rate_sec =
+ ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec);
+
+ if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) {
+ target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS;
+ initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_udp_payload_size);
+ cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64
+ " min_rtt=%" PRIu64,
+ cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt);
+ }
+}
+
+void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx) {
+ ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ reno_cc_reset(cc);
+}
+
+static void cubic_cc_reset(ngtcp2_cubic_cc *cc) {
+ cc->max_delivery_rate_sec = 0;
+ cc->target_cwnd = 0;
+ cc->w_last_max = 0;
+ cc->w_tcp = 0;
+ cc->origin_point = 0;
+ cc->epoch_start = UINT64_MAX;
+ cc->k = 0;
+
+ cc->rtt_sample_count = 0;
+ cc->current_round_min_rtt = UINT64_MAX;
+ cc->last_round_min_rtt = UINT64_MAX;
+ cc->window_end = -1;
+}
+
+void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log) {
+ cc->ccb.log = log;
+ cubic_cc_reset(cc);
+}
+
+void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc) { (void)cc; }
+
+int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem) {
+ ngtcp2_cubic_cc *cubic_cc;
+
+ cubic_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_cubic_cc));
+ if (cubic_cc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_cubic_cc_init(cubic_cc, 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_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;
+ cc->new_rtt_sample = ngtcp2_cc_cubic_cc_new_rtt_sample;
+ cc->reset = ngtcp2_cc_cubic_cc_reset;
+ cc->event = ngtcp2_cc_cubic_cc_event;
+
+ return 0;
+}
+
+void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_cubic_cc *cubic_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_cubic_cc, ccb);
+
+ ngtcp2_cubic_cc_free(cubic_cc);
+ ngtcp2_mem_free(mem, cubic_cc);
+}
+
+static uint64_t ngtcp2_cbrt(uint64_t n) {
+ int d;
+ uint64_t a;
+
+ if (n == 0) {
+ return 0;
+ }
+
+#if defined(_MSC_VER)
+# if defined(_M_X64)
+ d = (int)__lzcnt64(n);
+# elif defined(_M_ARM64)
+ {
+ unsigned long index;
+ d = sizeof(uint64_t) * CHAR_BIT;
+ if (_BitScanReverse64(&index, n)) {
+ d = d - 1 - index;
+ }
+ }
+# else
+ if ((n >> 32) != 0) {
+ d = __lzcnt((unsigned int)(n >> 32));
+ } else {
+ d = 32 + __lzcnt((unsigned int)n);
+ }
+# endif
+#else
+ d = __builtin_clzll(n);
+#endif
+ a = 1ULL << ((64 - d) / 3 + 1);
+
+ for (; a * a * a > n;) {
+ a = (2 * a + n / a / a) / 3;
+ }
+ return a;
+}
+
+/* HyStart++ constants */
+#define NGTCP2_HS_MIN_SSTHRESH 16
+#define NGTCP2_HS_N_RTT_SAMPLE 8
+#define NGTCP2_HS_MIN_ETA (4 * NGTCP2_MILLISECONDS)
+#define NGTCP2_HS_MAX_ETA (16 * NGTCP2_MILLISECONDS)
+
+void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ ngtcp2_duration t, min_rtt, eta;
+ uint64_t target;
+ uint64_t tx, kx, time_delta, delta;
+ uint64_t add, tcp_add;
+ uint64_t m;
+
+ if (pkt->pktns_id == NGTCP2_PKTNS_ID_APPLICATION && cc->window_end != -1 &&
+ cc->window_end <= pkt->pkt_num) {
+ cc->window_end = -1;
+ }
+
+ if (in_congestion_recovery(cstat, pkt->ts_sent)) {
+ return;
+ }
+
+ if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) {
+ return;
+ }
+
+ if (cstat->cwnd < cstat->ssthresh) {
+ /* slow-start */
+ cstat->cwnd += pkt->pktlen;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
+ pkt->pkt_num, cstat->cwnd);
+
+ if (cc->last_round_min_rtt != UINT64_MAX &&
+ cc->current_round_min_rtt != UINT64_MAX &&
+ cstat->cwnd >= NGTCP2_HS_MIN_SSTHRESH * cstat->max_udp_payload_size &&
+ cc->rtt_sample_count >= NGTCP2_HS_N_RTT_SAMPLE) {
+ eta = cc->last_round_min_rtt / 8;
+
+ if (eta < NGTCP2_HS_MIN_ETA) {
+ eta = NGTCP2_HS_MIN_ETA;
+ } else if (eta > NGTCP2_HS_MAX_ETA) {
+ eta = NGTCP2_HS_MAX_ETA;
+ }
+
+ if (cc->current_round_min_rtt >= cc->last_round_min_rtt + eta) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "HyStart++ exit slow start");
+
+ cc->w_last_max = cstat->cwnd;
+ cstat->ssthresh = cstat->cwnd;
+ }
+ }
+
+ return;
+ }
+
+ /* congestion avoidance */
+
+ if (cc->epoch_start == UINT64_MAX) {
+ cc->epoch_start = ts;
+ if (cstat->cwnd < cc->w_last_max) {
+ cc->k = ngtcp2_cbrt((cc->w_last_max - cstat->cwnd) * 10 / 4 /
+ cstat->max_udp_payload_size);
+ cc->origin_point = cc->w_last_max;
+ } else {
+ cc->k = 0;
+ cc->origin_point = cstat->cwnd;
+ }
+
+ cc->w_tcp = cstat->cwnd;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "cubic-ca epoch_start=%" PRIu64 " k=%" PRIu64
+ " origin_point=%" PRIu64,
+ cc->epoch_start, cc->k, cc->origin_point);
+
+ cc->pending_add = 0;
+ cc->pending_w_add = 0;
+ }
+
+ min_rtt = cstat->min_rtt == UINT64_MAX ? cstat->initial_rtt : cstat->min_rtt;
+
+ t = ts + min_rtt - cc->epoch_start;
+
+ tx = (t << 4) / NGTCP2_SECONDS;
+ kx = (cc->k << 4);
+
+ if (tx > kx) {
+ time_delta = tx - kx;
+ } else {
+ time_delta = kx - tx;
+ }
+
+ delta = cstat->max_udp_payload_size *
+ ((((time_delta * time_delta) >> 4) * time_delta) >> 8) * 4 / 10;
+
+ if (tx > kx) {
+ target = cc->origin_point + delta;
+ } else {
+ target = cc->origin_point - delta;
+ }
+
+ if (target > cstat->cwnd) {
+ m = cc->pending_add + cstat->max_udp_payload_size * (target - cstat->cwnd);
+ add = m / cstat->cwnd;
+ cc->pending_add = m % cstat->cwnd;
+ } else {
+ m = cc->pending_add + cstat->max_udp_payload_size;
+ add = m / (100 * cstat->cwnd);
+ cc->pending_add = m % (100 * cstat->cwnd);
+ }
+
+ m = cc->pending_w_add + cstat->max_udp_payload_size * pkt->pktlen;
+
+ cc->w_tcp += m / cstat->cwnd;
+ cc->pending_w_add = m % cstat->cwnd;
+
+ if (cc->w_tcp > cstat->cwnd) {
+ tcp_add =
+ cstat->max_udp_payload_size * (cc->w_tcp - cstat->cwnd) / cstat->cwnd;
+ if (tcp_add > add) {
+ add = tcp_add;
+ }
+ }
+
+ cstat->cwnd += add;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " acked, cubic-ca cwnd=%" PRIu64 " t=%" PRIu64
+ " k=%" PRIi64 " time_delta=%" PRIu64 " delta=%" PRIu64
+ " target=%" PRIu64 " w_tcp=%" PRIu64,
+ pkt->pkt_num, cstat->cwnd, t, cc->k, time_delta >> 4, delta,
+ target, cc->w_tcp);
+}
+
+void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts_sent,
+ 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)) {
+ return;
+ }
+
+ cstat->congestion_recovery_start_ts = ts;
+
+ cc->epoch_start = UINT64_MAX;
+ if (cstat->cwnd < cc->w_last_max) {
+ cc->w_last_max = cstat->cwnd * 17 / 10 / 2;
+ } else {
+ cc->w_last_max = cstat->cwnd;
+ }
+
+ min_cwnd = 2 * cstat->max_udp_payload_size;
+ cstat->ssthresh = cstat->cwnd * 7 / 10;
+ cstat->ssthresh = ngtcp2_max(cstat->ssthresh, min_cwnd);
+ cstat->cwnd = cstat->ssthresh;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "reduce cwnd because of packet loss cwnd=%" PRIu64,
+ cstat->cwnd);
+}
+
+void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)ts;
+
+ cstat->cwnd = 2 * cstat->max_udp_payload_size;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+}
+
+void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ uint64_t target_cwnd, initcwnd;
+ (void)ts;
+
+ /* TODO Use sliding window for min rtt measurement */
+ /* TODO Use sliding window */
+ cc->max_delivery_rate_sec =
+ ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec);
+
+ if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) {
+ target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS;
+ initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_udp_payload_size);
+ cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64
+ " min_rtt=%" PRIu64,
+ cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt);
+ }
+}
+
+void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ (void)cstat;
+
+ if (pkt->pktns_id != NGTCP2_PKTNS_ID_APPLICATION || cc->window_end != -1) {
+ return;
+ }
+
+ cc->window_end = pkt->pkt_num;
+ cc->last_round_min_rtt = cc->current_round_min_rtt;
+ cc->current_round_min_rtt = UINT64_MAX;
+ cc->rtt_sample_count = 0;
+}
+
+void ngtcp2_cc_cubic_cc_new_rtt_sample(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 (cc->window_end == -1) {
+ return;
+ }
+
+ cc->current_round_min_rtt =
+ ngtcp2_min(cc->current_round_min_rtt, cstat->latest_rtt);
+ ++cc->rtt_sample_count;
+}
+
+void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ cubic_cc_reset(cc);
+}
+
+void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ ngtcp2_tstamp last_ts;
+
+ if (event != NGTCP2_CC_EVENT_TYPE_TX_START || cc->epoch_start == UINT64_MAX) {
+ return;
+ }
+
+ last_ts = cstat->last_tx_pkt_ts[NGTCP2_PKTNS_ID_APPLICATION];
+ if (last_ts == UINT64_MAX || last_ts <= cc->epoch_start) {
+ return;
+ }
+
+ assert(ts >= last_ts);
+
+ cc->epoch_start += ts - last_ts;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h
new file mode 100644
index 0000000000..e1ca759480
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h
@@ -0,0 +1,135 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CC_H
+#define NGTCP2_CC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#define NGTCP2_LOSS_REDUCTION_FACTOR_BITS 1
+#define NGTCP2_PERSISTENT_CONGESTION_THRESHOLD 3
+
+typedef struct ngtcp2_log ngtcp2_log;
+
+/*
+ * ngtcp2_cc_compute_initcwnd computes initial cwnd.
+ */
+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_reno_cc is the RENO congestion controller. */
+typedef struct ngtcp2_reno_cc {
+ ngtcp2_cc_base ccb;
+ uint64_t max_delivery_rate_sec;
+ uint64_t target_cwnd;
+ uint64_t pending_add;
+} ngtcp2_reno_cc;
+
+int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log);
+
+void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc);
+
+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 ts);
+
+void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc);
+
+/* ngtcp2_cubic_cc is CUBIC congestion controller. */
+typedef struct ngtcp2_cubic_cc {
+ ngtcp2_cc_base ccb;
+ uint64_t max_delivery_rate_sec;
+ uint64_t target_cwnd;
+ uint64_t w_last_max;
+ uint64_t w_tcp;
+ uint64_t origin_point;
+ ngtcp2_tstamp epoch_start;
+ uint64_t k;
+ /* HyStart++ variables */
+ size_t rtt_sample_count;
+ uint64_t current_round_min_rtt;
+ uint64_t last_round_min_rtt;
+ int64_t window_end;
+ uint64_t pending_add;
+ uint64_t pending_w_add;
+} ngtcp2_cubic_cc;
+
+int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log);
+
+void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc);
+
+void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts_sent,
+ 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);
+
+void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt);
+
+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_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
+
+#endif /* NGTCP2_CC_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c
new file mode 100644
index 0000000000..cdf31f8f62
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c
@@ -0,0 +1,121 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_cid.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "ngtcp2_path.h"
+#include "ngtcp2_str.h"
+
+void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; }
+
+void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) {
+ assert(datalen <= NGTCP2_MAX_CIDLEN);
+
+ cid->datalen = datalen;
+ if (datalen) {
+ ngtcp2_cpymem(cid->data, data, datalen);
+ }
+}
+
+int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other) {
+ return cid->datalen == other->datalen &&
+ 0 == memcmp(cid->data, other->data, cid->datalen);
+}
+
+int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) {
+ int s = lhs->datalen < rhs->datalen;
+ size_t n = s ? lhs->datalen : rhs->datalen;
+ int c = memcmp(lhs->data, rhs->data, n);
+
+ return c < 0 || (c == 0 && s);
+}
+
+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) {
+ scid->pe.index = NGTCP2_PQ_BAD_INDEX;
+ scid->seq = seq;
+ scid->cid = *cid;
+ scid->ts_retired = 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;
+ dest->flags = src->flags;
+}
+
+void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
+ const uint8_t *token) {
+ dcid->seq = seq;
+ dcid->cid = *cid;
+ if (token) {
+ memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ } else {
+ memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN);
+ }
+ ngtcp2_path_storage_zero(&dcid->ps);
+ dcid->ts_retired = UINT64_MAX;
+ dcid->flags = NGTCP2_DCID_FLAG_NONE;
+ dcid->bytes_sent = 0;
+ dcid->bytes_recv = 0;
+}
+
+void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
+ ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token);
+ ngtcp2_path_copy(&dest->ps.path, &src->ps.path);
+ dest->ts_retired = src->ts_retired;
+ dest->flags = src->flags;
+ dest->bytes_sent = src->bytes_sent;
+ dest->bytes_recv = src->bytes_recv;
+}
+
+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);
+}
+
+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) &&
+ memcmp(dcid->token, token,
+ NGTCP2_STATELESS_RESET_TOKENLEN) == 0
+ ? 0
+ : NGTCP2_ERR_PROTO;
+ }
+
+ return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h
new file mode 100644
index 0000000000..a356b432d4
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h
@@ -0,0 +1,153 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CID_H
+#define NGTCP2_CID_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#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_USED indicates that a local endpoint observed that
+ a remote endpoint uses a particular Connection ID. */
+#define NGTCP2_SCID_FLAG_USED 0x01
+/* NGTCP2_SCID_FLAG_RETIRED indicates that a particular Connection ID
+ is retired. */
+#define NGTCP2_SCID_FLAG_RETIRED 0x02
+
+typedef struct ngtcp2_scid {
+ ngtcp2_pq_entry pe;
+ /* seq is the sequence number associated to the CID. */
+ uint64_t seq;
+ /* cid is a connection ID */
+ ngtcp2_cid cid;
+ /* ts_retired is the timestamp when peer tells that this CID is
+ retired. */
+ ngtcp2_tstamp ts_retired;
+ /* 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
+/* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path
+ has been validated. */
+#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01
+
+typedef struct ngtcp2_dcid {
+ /* seq is the sequence number associated to the CID. */
+ uint64_t seq;
+ /* cid is a connection ID */
+ ngtcp2_cid cid;
+ /* 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. */
+ ngtcp2_tstamp ts_retired;
+ /* 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;
+ /* 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.
+ Actually, the stateless reset token is tied to the connection,
+ not to the particular connection ID. */
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+} ngtcp2_dcid;
+
+/* ngtcp2_cid_zero makes |cid| zero-length. */
+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|.
+ */
+int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs);
+
+/*
+ * ngtcp2_cid_empty returns nonzero if |cid| includes empty connection
+ * ID.
+ */
+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.
+ */
+void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid,
+ const uint8_t *token);
+
+/*
+ * ngtcp2_scid_copy copies |src| into |dest|.
+ */
+void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src);
+
+/*
+ * ngtcp2_dcid_init initializes |dcid| with the given parameters. If
+ * |token| is NULL, the function fills dcid->token it with 0. |token|
+ * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
+ */
+void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
+ const uint8_t *token);
+
+/*
+ * ngtcp2_dcid_copy copies |src| into |dest|.
+ */
+void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
+
+/*
+ * ngtcp2_dcid_copy_cid_token behaves like ngtcp2_dcid_copy, but it
+ * only copies cid, seq, and path.
+ */
+void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
+
+/*
+ * ngtcp2_dcid_verify_uniqueness verifies uniqueness of (|seq|, |cid|,
+ * |token|) tuple against |dcid|.
+ */
+int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
+ const ngtcp2_cid *cid, 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
new file mode 100644
index 0000000000..c8b1d15db5
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c
@@ -0,0 +1,11236 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_conn.h"
+
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_log.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_addr.h"
+#include "ngtcp2_path.h"
+#include "ngtcp2_rcvry.h"
+
+/* NGTCP2_FLOW_WINDOW_RTT_FACTOR is the factor of RTT when flow
+ control window auto-tuning is triggered. */
+#define NGTCP2_FLOW_WINDOW_RTT_FACTOR 2
+/* NGTCP2_FLOW_WINDOW_SCALING_FACTOR is the growth factor of flow
+ control window. */
+#define NGTCP2_FLOW_WINDOW_SCALING_FACTOR 2
+
+/*
+ * conn_local_stream returns nonzero if |stream_id| indicates that it
+ * is the stream initiated by local endpoint.
+ */
+static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) {
+ return (uint8_t)(stream_id & 1) == conn->server;
+}
+
+/*
+ * bidi_stream returns nonzero if |stream_id| is a bidirectional
+ * stream ID.
+ */
+static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; }
+
+static int conn_call_recv_client_initial(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid) {
+ int rv;
+
+ assert(conn->callbacks.recv_client_initial);
+
+ rv = conn->callbacks.recv_client_initial(conn, dcid, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_handshake_completed(ngtcp2_conn *conn) {
+ int rv;
+
+ if (!conn->callbacks.handshake_completed) {
+ return 0;
+ }
+
+ rv = conn->callbacks.handshake_completed(conn, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint32_t flags, uint64_t offset,
+ const uint8_t *data, size_t datalen) {
+ int rv;
+
+ if (!conn->callbacks.recv_stream_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_stream_data(conn, flags, strm->stream_id, offset,
+ data, datalen, conn->user_data,
+ strm->stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen) {
+ int rv;
+
+ assert(conn->callbacks.recv_crypto_data);
+
+ rv = conn->callbacks.recv_crypto_data(conn, crypto_level, offset, data,
+ datalen, conn->user_data);
+ switch (rv) {
+ case 0:
+ case NGTCP2_ERR_CRYPTO:
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ case NGTCP2_ERR_PROTO:
+ case NGTCP2_ERR_CALLBACK_FAILURE:
+ return rv;
+ default:
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+}
+
+static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ int rv;
+
+ if (!conn->callbacks.stream_open) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stream_open(conn, strm->stream_id, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ int rv;
+
+ 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 (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_stream_reset(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *stream_user_data) {
+ int rv;
+
+ if (!conn->callbacks.stream_reset) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stream_reset(conn, stream_id, final_size, app_error_code,
+ conn->user_data, stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_local_streams_bidi(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_local_streams_bidi) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_local_streams_bidi(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_local_streams_uni(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_local_streams_uni) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_local_streams_uni(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen) {
+ int rv;
+
+ assert(conn->callbacks.get_new_connection_id);
+
+ rv = conn->callbacks.get_new_connection_id(conn, cid, token, cidlen,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_remove_connection_id(ngtcp2_conn *conn,
+ const ngtcp2_cid *cid) {
+ int rv;
+
+ if (!conn->callbacks.remove_connection_id) {
+ return 0;
+ }
+
+ rv = conn->callbacks.remove_connection_id(conn, cid, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path,
+ ngtcp2_path_validation_result res) {
+ int rv;
+
+ if (!conn->callbacks.path_validation) {
+ return 0;
+ }
+
+ rv = conn->callbacks.path_validation(conn, path, res, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_select_preferred_addr(ngtcp2_conn *conn,
+ ngtcp2_addr *dest) {
+ int rv;
+
+ if (!conn->callbacks.select_preferred_addr) {
+ return 0;
+ }
+
+ assert(conn->remote.transport_params.preferred_address_present);
+
+ rv = conn->callbacks.select_preferred_addr(
+ conn, dest, &conn->remote.transport_params.preferred_address,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_remote_streams_bidi(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_remote_streams_bidi) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_remote_streams_bidi(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_remote_streams_uni(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_remote_streams_uni) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_remote_streams_uni(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_stream_data(ngtcp2_conn *conn,
+ ngtcp2_strm *strm,
+ int64_t stream_id,
+ uint64_t datalen) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_stream_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_stream_data(
+ conn, stream_id, datalen, conn->user_data, strm->stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_dcid_status(ngtcp2_conn *conn,
+ ngtcp2_connection_id_status_type type,
+ const ngtcp2_dcid *dcid) {
+ int rv;
+
+ if (!conn->callbacks.dcid_status) {
+ return 0;
+ }
+
+ rv = conn->callbacks.dcid_status(
+ conn, (int)type, dcid->seq, &dcid->cid,
+ ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL
+ : dcid->token,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_activate_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) {
+ return conn_call_dcid_status(conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE,
+ dcid);
+}
+
+static int conn_call_deactivate_dcid(ngtcp2_conn *conn,
+ const ngtcp2_dcid *dcid) {
+ return conn_call_dcid_status(
+ conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid);
+}
+
+static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (!aead_ctx->native_handle) {
+ return;
+ }
+
+ assert(conn->callbacks.delete_crypto_aead_ctx);
+
+ conn->callbacks.delete_crypto_aead_ctx(conn, aead_ctx, conn->user_data);
+}
+
+static void
+conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn,
+ ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (!cipher_ctx->native_handle) {
+ return;
+ }
+
+ assert(conn->callbacks.delete_crypto_cipher_ctx);
+
+ conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data);
+}
+
+static int crypto_offset_less(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs < *(int64_t *)rhs;
+}
+
+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) {
+ int rv;
+
+ memset(pktns, 0, sizeof(*pktns));
+
+ rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns->tx.last_pkt_num = -1;
+ pktns->rx.max_pkt_num = -1;
+ pktns->rx.max_ack_eliciting_pkt_num = -1;
+
+ rv = ngtcp2_acktr_init(&pktns->acktr, log, mem);
+ if (rv != 0) {
+ 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;
+ }
+
+ rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less,
+ sizeof(uint64_t), mem);
+ if (rv != 0) {
+ goto fail_tx_frq_init;
+ }
+
+ ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log,
+ qlog, 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);
+
+ return rv;
+}
+
+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) {
+ int rv;
+
+ *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns));
+ if (*ppktns == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem);
+ if (rv != 0) {
+ ngtcp2_mem_free(mem, *ppktns);
+ }
+
+ return rv;
+}
+
+static int cycle_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) {
+ ngtcp2_strm *ls = ngtcp2_struct_of(lhs, ngtcp2_strm, pe);
+ ngtcp2_strm *rs = ngtcp2_struct_of(rhs, ngtcp2_strm, pe);
+
+ if (ls->cycle == rs->cycle) {
+ return ls->stream_id < rs->stream_id;
+ }
+
+ return rs->cycle - ls->cycle <= 1;
+}
+
+static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) {
+ ngtcp2_pkt_chain *next;
+
+ for (; pc;) {
+ next = pc->next;
+ ngtcp2_pkt_chain_del(pc, mem);
+ pc = next;
+ }
+}
+
+static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_ksl_it it;
+
+ delete_buffed_pkts(pktns->rx.buffed_pkts, mem);
+
+ ngtcp2_frame_chain_list_del(pktns->tx.frq, mem);
+
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem);
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, 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_ksl_free(&pktns->crypto.tx.frq);
+ ngtcp2_rtb_free(&pktns->rtb);
+ ngtcp2_strm_free(&pktns->crypto.strm);
+ ngtcp2_acktr_free(&pktns->acktr);
+ ngtcp2_gaptr_free(&pktns->rx.pngap);
+}
+
+static void pktns_del(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
+ if (pktns == NULL) {
+ return;
+ }
+
+ pktns_free(pktns, mem);
+
+ ngtcp2_mem_free(mem, pktns);
+}
+
+static void cc_del(ngtcp2_cc *cc, ngtcp2_cc_algo cc_algo,
+ const ngtcp2_mem *mem) {
+ switch (cc_algo) {
+ case NGTCP2_CC_ALGO_RENO:
+ ngtcp2_cc_reno_cc_free(cc, mem);
+ break;
+ case NGTCP2_CC_ALGO_CUBIC:
+ ngtcp2_cc_cubic_cc_free(cc, mem);
+ break;
+ default:
+ break;
+ }
+}
+
+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,
+ 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;
+}
+
+/*
+ * conn_reset_conn_stat_cc resets congestion state in |cstat|.
+ */
+static void conn_reset_conn_stat_cc(ngtcp2_conn *conn,
+ ngtcp2_conn_stat *cstat) {
+ cstat->latest_rtt = 0;
+ cstat->min_rtt = UINT64_MAX;
+ cstat->smoothed_rtt = conn->local.settings.initial_rtt;
+ cstat->rttvar = conn->local.settings.initial_rtt / 2;
+ cstat->first_rtt_sample_ts = UINT64_MAX;
+ cstat->pto_count = 0;
+ cstat->loss_detection_timer = UINT64_MAX;
+ cstat->cwnd =
+ ngtcp2_cc_compute_initcwnd(conn->local.settings.max_udp_payload_size);
+ cstat->ssthresh = UINT64_MAX;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+ cstat->bytes_in_flight = 0;
+ cstat->delivery_rate_sec = 0;
+}
+
+/*
+ * reset_conn_stat_recovery resets the fields related to the recovery
+ * function
+ */
+static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) {
+ // 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));
+}
+
+/*
+ * conn_reset_conn_stat resets |cstat|. The following fields are not
+ * reset: initial_rtt and max_udp_payload_size.
+ */
+static void conn_reset_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) {
+ conn_reset_conn_stat_cc(conn, cstat);
+ reset_conn_stat_recovery(cstat);
+}
+
+static void delete_scid(ngtcp2_ksl *scids, const ngtcp2_mem *mem) {
+ ngtcp2_ksl_it it;
+
+ for (it = ngtcp2_ksl_begin(scids); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_mem_free(mem, ngtcp2_ksl_it_get(&it));
+ }
+}
+
+/*
+ * compute_pto computes PTO.
+ */
+static ngtcp2_duration compute_pto(ngtcp2_duration smoothed_rtt,
+ ngtcp2_duration rttvar,
+ ngtcp2_duration max_ack_delay) {
+ ngtcp2_duration var = ngtcp2_max(4 * rttvar, NGTCP2_GRANULARITY);
+ return smoothed_rtt + var + max_ack_delay;
+}
+
+/*
+ * conn_compute_initial_pto computes PTO using the initial 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;
+ return compute_pto(initial_rtt, initial_rtt / 2, max_ack_delay);
+}
+
+/*
+ * conn_compute_pto computes the current PTO.
+ */
+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;
+ return compute_pto(cstat->smoothed_rtt, cstat->rttvar, max_ack_delay);
+}
+
+static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *prtb_entry_flags, ngtcp2_pktns *pktns,
+ const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) {
+ assert(pi);
+
+ if (pi->ecn != NGTCP2_ECN_NOT_ECT) {
+ /* We have already made a transition of validation state and
+ deceided to send UDP datagram with ECN bit set. Coalesced QUIC
+ packets also bear ECN bits set. */
+ if (pktns->tx.ecn.start_pkt_num == INT64_MAX) {
+ pktns->tx.ecn.start_pkt_num = hd->pkt_num;
+ }
+
+ ++pktns->tx.ecn.validation_pkt_sent;
+
+ if (prtb_entry_flags) {
+ *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN;
+ }
+
+ ++pktns->tx.ecn.ect0;
+
+ return;
+ }
+
+ switch (conn->tx.ecn.state) {
+ case NGTCP2_ECN_STATE_TESTING:
+ if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
+ assert(0 == pktns->tx.ecn.validation_pkt_sent);
+ assert(0 == pktns->tx.ecn.validation_pkt_lost);
+
+ conn->tx.ecn.validation_start_ts = ts;
+ } else if (ts - conn->tx.ecn.validation_start_ts >=
+ 3 * conn_compute_pto(conn, pktns)) {
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+ break;
+ }
+
+ if (pktns->tx.ecn.start_pkt_num == INT64_MAX) {
+ pktns->tx.ecn.start_pkt_num = hd->pkt_num;
+ }
+
+ ++pktns->tx.ecn.validation_pkt_sent;
+
+ if (++conn->tx.ecn.dgram_sent == NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS) {
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+ }
+ /* fall through */
+ case NGTCP2_ECN_STATE_CAPABLE:
+ /* pi is provided per UDP datagram. */
+ assert(NGTCP2_ECN_NOT_ECT == pi->ecn);
+
+ pi->ecn = NGTCP2_ECN_ECT_0;
+
+ if (prtb_entry_flags) {
+ *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN;
+ }
+
+ ++pktns->tx.ecn.ect0;
+ break;
+ case NGTCP2_ECN_STATE_UNKNOWN:
+ case NGTCP2_ECN_STATE_FAILED:
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_TESTING;
+ conn->tx.ecn.validation_start_ts = UINT64_MAX;
+ conn->tx.ecn.dgram_sent = 0;
+
+ if (in_pktns) {
+ in_pktns->tx.ecn.start_pkt_num = INT64_MAX;
+ in_pktns->tx.ecn.validation_pkt_sent = 0;
+ in_pktns->tx.ecn.validation_pkt_lost = 0;
+ }
+
+ if (hs_pktns) {
+ hs_pktns->tx.ecn.start_pkt_num = INT64_MAX;
+ hs_pktns->tx.ecn.validation_pkt_sent = 0;
+ hs_pktns->tx.ecn.validation_pkt_lost = 0;
+ }
+
+ pktns->tx.ecn.start_pkt_num = INT64_MAX;
+ pktns->tx.ecn.validation_pkt_sent = 0;
+ pktns->tx.ecn.validation_pkt_lost = 0;
+}
+
+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,
+ const ngtcp2_settings *settings,
+ const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data, int server) {
+ int rv;
+ ngtcp2_scid *scident;
+ uint8_t *buf;
+
+ assert(settings->max_window <= NGTCP2_MAX_VARINT);
+ assert(settings->max_stream_window <= NGTCP2_MAX_VARINT);
+ 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);
+
+ if (mem == NULL) {
+ mem = ngtcp2_mem_default();
+ }
+
+ *pconn = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_conn));
+ if (*pconn == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ 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;
+ }
+
+ rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE,
+ sizeof(ngtcp2_dcid), mem);
+ if (rv != 0) {
+ goto fail_dcid_unused_init;
+ }
+
+ rv =
+ ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
+ sizeof(ngtcp2_dcid), mem);
+ if (rv != 0) {
+ goto fail_dcid_retired_init;
+ }
+
+ rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem);
+ if (rv != 0) {
+ goto fail_seqgap_init;
+ }
+
+ rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem);
+ if (rv != 0) {
+ goto fail_scid_set_init;
+ }
+
+ ngtcp2_pq_init(&(*pconn)->scid.used, ts_retired_less, mem);
+
+ rv = ngtcp2_map_init(&(*pconn)->strms, mem);
+ if (rv != 0) {
+ goto fail_strms_init;
+ }
+
+ 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;
+ }
+
+ rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem);
+ if (rv != 0) {
+ goto fail_remote_uni_idtr_init;
+ }
+
+ 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_log_init(&(*pconn)->log, scid, settings->log_printf,
+ settings->initial_ts, user_data);
+ ngtcp2_qlog_init(&(*pconn)->qlog, settings->qlog.write, settings->initial_ts,
+ user_data);
+ if ((*pconn)->qlog.write) {
+ buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN);
+ if (buf == NULL) {
+ 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) {
+ goto fail_token;
+ }
+ memcpy(buf, settings->token.base, settings->token.len);
+ (*pconn)->local.settings.token.base = buf;
+ } else {
+ (*pconn)->local.settings.token.base = NULL;
+ (*pconn)->local.settings.token.len = 0;
+ }
+
+ if (settings->max_udp_payload_size == 0) {
+ (*pconn)->local.settings.max_udp_payload_size = NGTCP2_DEFAULT_MAX_PKTLEN;
+ }
+
+ conn_reset_conn_stat(*pconn, &(*pconn)->cstat);
+ (*pconn)->cstat.initial_rtt = settings->initial_rtt;
+ (*pconn)->cstat.max_udp_payload_size =
+ (*pconn)->local.settings.max_udp_payload_size;
+
+ ngtcp2_rst_init(&(*pconn)->rst);
+
+ (*pconn)->cc_algo = settings->cc_algo;
+
+ switch (settings->cc_algo) {
+ case NGTCP2_CC_ALGO_RENO:
+ rv = ngtcp2_cc_reno_cc_init(&(*pconn)->cc, &(*pconn)->log, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
+ break;
+ case NGTCP2_CC_ALGO_CUBIC:
+ rv = ngtcp2_cc_cubic_cc_init(&(*pconn)->cc, &(*pconn)->log, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
+ break;
+ case NGTCP2_CC_ALGO_CUSTOM:
+ assert(settings->cc);
+ (*pconn)->cc = *settings->cc;
+ (*pconn)->cc.ccb->log = &(*pconn)->log;
+ break;
+ default:
+ assert(0);
+ }
+
+ rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst,
+ &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, 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);
+ 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);
+ if (rv != 0) {
+ goto fail_pktns_init;
+ }
+
+ scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
+ if (scident == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail_scident;
+ }
+
+ /* Set stateless reset token later if it is available in the local
+ transport parameters */
+ ngtcp2_scid_init(scident, 0, scid, NULL);
+
+ rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident);
+ if (rv != 0) {
+ goto fail_scid_set_insert;
+ }
+
+ scident = NULL;
+
+ ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL);
+ ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path);
+
+ rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1);
+ if (rv != 0) {
+ goto fail_seqgap_push;
+ }
+
+ (*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)->early.discard_started_ts = UINT64_MAX;
+
+ conn_reset_ecn_validation_state(*pconn);
+
+ ngtcp2_qlog_start(&(*pconn)->qlog, server ? &settings->qlog.odcid : dcid,
+ server);
+
+ return 0;
+
+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);
+fail_hs_pktns_init:
+ pktns_del((*pconn)->in_pktns, mem);
+fail_in_pktns_init:
+ cc_del(&(*pconn)->cc, settings->cc_algo, mem);
+fail_cc_init:
+ 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_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 rv;
+ rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params,
+ mem, user_data, 0);
+ if (rv != 0) {
+ return rv;
+ }
+ (*pconn)->rcid = *dcid;
+ (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL;
+ (*pconn)->local.bidi.next_stream_id = 0;
+ (*pconn)->local.uni.next_stream_id = 2;
+
+ rv = ngtcp2_conn_commit_local_transport_params(*pconn);
+ if (rv != 0) {
+ ngtcp2_conn_del(*pconn);
+ return rv;
+ }
+
+ 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 rv;
+ rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, 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;
+
+ if ((*pconn)->local.settings.token.len) {
+ /* Usage of token lifts amplification limit */
+ (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_fc_credits returns the number of bytes allowed to be sent to
+ * the given stream. Both connection and stream level flow control
+ * credits are considered.
+ */
+static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ return ngtcp2_min(strm->tx.max_offset - strm->tx.offset,
+ conn->tx.max_offset - conn->tx.offset);
+}
+
+/*
+ * conn_enforce_flow_control returns the number of bytes allowed to be
+ * 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) {
+ uint64_t fc_credits = conn_fc_credits(conn, strm);
+ return (size_t)ngtcp2_min((uint64_t)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);
+
+ ngtcp2_strm_free(s);
+ ngtcp2_mem_free(mem, s);
+
+ return 0;
+}
+
+void ngtcp2_conn_del(ngtcp2_conn *conn) {
+ if (conn == NULL) {
+ return;
+ }
+
+ ngtcp2_qlog_end(&conn->qlog);
+
+ if (conn->early.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx);
+
+ if (conn->crypto.key_update.old_rx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx);
+ }
+ if (conn->crypto.key_update.new_rx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.new_rx_ckm->aead_ctx);
+ }
+ if (conn->crypto.key_update.new_tx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.new_tx_ckm->aead_ctx);
+ }
+
+ if (conn->pktns.crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn,
+ &conn->pktns.crypto.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.rx.hp_ctx);
+
+ if (conn->pktns.crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn,
+ &conn->pktns.crypto.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.tx.hp_ctx);
+
+ if (conn->hs_pktns) {
+ if (conn->hs_pktns->crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->hs_pktns->crypto.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.rx.hp_ctx);
+
+ if (conn->hs_pktns->crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->hs_pktns->crypto.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.tx.hp_ctx);
+ }
+ if (conn->in_pktns) {
+ if (conn->in_pktns->crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->in_pktns->crypto.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.rx.hp_ctx);
+
+ if (conn->in_pktns->crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->in_pktns->crypto.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.tx.hp_ctx);
+ }
+
+ conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx);
+
+ 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);
+
+ ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->crypto.key_update.new_rx_ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->crypto.key_update.new_tx_ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
+
+ pktns_free(&conn->pktns, conn->mem);
+ pktns_del(conn->hs_pktns, conn->mem);
+ pktns_del(conn->in_pktns, conn->mem);
+
+ cc_del(&conn->cc, conn->cc_algo, conn->mem);
+
+ ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin);
+
+ ngtcp2_ringbuf_free(&conn->rx.path_challenge);
+
+ ngtcp2_pv_del(conn->pv);
+
+ 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_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_mem_free(conn->mem, conn);
+}
+
+/*
+ * conn_ensure_ack_blks makes sure that conn->tx.ack->ack.blks can
+ * contain at least |n| additional ngtcp2_ack_blk.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_ensure_ack_blks(ngtcp2_conn *conn, size_t n) {
+ ngtcp2_frame *fr;
+ size_t max = conn->tx.max_ack_blks;
+
+ if (n <= max) {
+ return 0;
+ }
+
+ max *= 2;
+
+ assert(max >= n);
+
+ fr = ngtcp2_mem_realloc(conn->mem, conn->tx.ack,
+ sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_blk) * max);
+ if (fr == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ conn->tx.ack = fr;
+ conn->tx.max_ack_blks = max;
+
+ return 0;
+}
+
+/*
+ * conn_compute_ack_delay computes ACK delay for outgoing protected
+ * ACK.
+ */
+static ngtcp2_duration conn_compute_ack_delay(ngtcp2_conn *conn) {
+ return ngtcp2_min(conn->local.transport_params.max_ack_delay,
+ conn->cstat.smoothed_rtt / 8);
+}
+
+/*
+ * conn_create_ack_frame creates ACK frame, and assigns its pointer to
+ * |*pfr| if there are any received packets to acknowledge. If there
+ * are no packets to acknowledge, this function returns 0, and |*pfr|
+ * is untouched. The caller is advised to set |*pfr| to NULL before
+ * calling this function, and check it after this function returns.
+ * If |nodelay| is nonzero, delayed ACK timer is ignored.
+ *
+ * The memory for ACK frame is dynamically allocated by this function.
+ * A caller is responsible to free it.
+ *
+ * Call ngtcp2_acktr_commit_ack after a created ACK frame is
+ * successfully serialized into a packet.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr,
+ ngtcp2_pktns *pktns, uint8_t type,
+ ngtcp2_tstamp ts, ngtcp2_duration ack_delay,
+ uint64_t ack_delay_exponent) {
+ /* TODO Measure an actual size of ACK blocks to find the best
+ default value. */
+ const size_t initial_max_ack_blks = 8;
+ int64_t last_pkt_num;
+ ngtcp2_acktr *acktr = &pktns->acktr;
+ ngtcp2_ack_blk *blk;
+ ngtcp2_ksl_it it;
+ ngtcp2_acktr_entry *rpkt;
+ ngtcp2_ack *ack;
+ size_t blk_idx;
+ ngtcp2_tstamp largest_ack_ts;
+ int rv;
+
+ if (acktr->flags & NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK) {
+ ack_delay = 0;
+ }
+
+ if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) {
+ return 0;
+ }
+
+ it = ngtcp2_acktr_get(acktr);
+ if (ngtcp2_ksl_it_end(&it)) {
+ ngtcp2_acktr_commit_ack(acktr);
+ return 0;
+ }
+
+ if (conn->tx.ack == NULL) {
+ conn->tx.ack = ngtcp2_mem_malloc(
+ conn->mem,
+ sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_blk) * initial_max_ack_blks);
+ if (conn->tx.ack == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ conn->tx.max_ack_blks = initial_max_ack_blks;
+ }
+
+ ack = &conn->tx.ack->ack;
+
+ if (pktns->rx.ecn.ect0 || pktns->rx.ecn.ect1 || pktns->rx.ecn.ce) {
+ ack->type = NGTCP2_FRAME_ACK_ECN;
+ ack->ecn.ect0 = pktns->rx.ecn.ect0;
+ ack->ecn.ect1 = pktns->rx.ecn.ect1;
+ ack->ecn.ce = pktns->rx.ecn.ce;
+ } else {
+ ack->type = NGTCP2_FRAME_ACK;
+ }
+ ack->num_blks = 0;
+
+ rpkt = ngtcp2_ksl_it_get(&it);
+
+ if (rpkt->pkt_num == pktns->rx.max_pkt_num) {
+ last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1);
+ largest_ack_ts = rpkt->tstamp;
+ ack->largest_ack = rpkt->pkt_num;
+ ack->first_ack_blklen = rpkt->len - 1;
+
+ ngtcp2_ksl_it_next(&it);
+ } else {
+ assert(rpkt->pkt_num < pktns->rx.max_pkt_num);
+
+ last_pkt_num = pktns->rx.max_pkt_num;
+ largest_ack_ts = pktns->rx.max_pkt_ts;
+ ack->largest_ack = pktns->rx.max_pkt_num;
+ ack->first_ack_blklen = 0;
+ }
+
+ if (type == NGTCP2_PKT_SHORT) {
+ ack->ack_delay_unscaled = ts - largest_ack_ts;
+ ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS /
+ (1ULL << ack_delay_exponent);
+ } else {
+ ack->ack_delay_unscaled = 0;
+ ack->ack_delay = 0;
+ }
+
+ for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
+ if (ack->num_blks == NGTCP2_MAX_ACK_BLKS) {
+ break;
+ }
+
+ rpkt = ngtcp2_ksl_it_get(&it);
+
+ blk_idx = ack->num_blks++;
+ rv = conn_ensure_ack_blks(conn, ack->num_blks);
+ if (rv != 0) {
+ return rv;
+ }
+ ack = &conn->tx.ack->ack;
+ blk = &ack->blks[blk_idx];
+ blk->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2);
+ blk->blklen = rpkt->len - 1;
+
+ last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1);
+ }
+
+ /* TODO Just remove entries which cannot fit into a single ACK frame
+ for now. */
+ if (!ngtcp2_ksl_it_end(&it)) {
+ ngtcp2_acktr_forget(acktr, ngtcp2_ksl_it_get(&it));
+ }
+
+ *pfr = conn->tx.ack;
+
+ return 0;
+}
+
+/*
+ * conn_ppe_write_frame writes |fr| to |ppe|. If |hd_logged| is not
+ * NULL and |*hd_logged| is zero, packet header is logged, and 1 is
+ * assigned to |*hd_logged|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too small.
+ */
+static int conn_ppe_write_frame_hd_log(ngtcp2_conn *conn, ngtcp2_ppe *ppe,
+ int *hd_logged, const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame *fr) {
+ int rv;
+
+ rv = ngtcp2_ppe_encode_frame(ppe, fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return rv;
+ }
+
+ if (hd_logged && !*hd_logged) {
+ *hd_logged = 1;
+ ngtcp2_log_tx_pkt_hd(&conn->log, hd);
+ ngtcp2_qlog_pkt_sent_start(&conn->qlog);
+ }
+
+ ngtcp2_log_tx_fr(&conn->log, hd, fr);
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+
+ return 0;
+}
+
+/*
+ * conn_ppe_write_frame writes |fr| to |ppe|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too small.
+ */
+static int conn_ppe_write_frame(ngtcp2_conn *conn, ngtcp2_ppe *ppe,
+ const ngtcp2_pkt_hd *hd, ngtcp2_frame *fr) {
+ return conn_ppe_write_frame_hd_log(conn, ppe, NULL, hd, fr);
+}
+
+/*
+ * conn_on_pkt_sent is called when new non-ACK-only packet is sent.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_rtb *rtb,
+ ngtcp2_rtb_entry *ent) {
+ int rv;
+
+ /* This function implements OnPacketSent, but it handles only
+ non-ACK-only packet. */
+ rv = ngtcp2_rtb_add(rtb, ent, &conn->cstat);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ conn->cstat.last_tx_pkt_ts[rtb->pktns_id] = ent->ts;
+ }
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ent->ts);
+
+ return 0;
+}
+
+/*
+ * pktns_select_pkt_numlen selects shortest packet number encoding for
+ * the next packet number based on the largest acknowledged packet
+ * number. It returns the number of bytes to encode the packet
+ * number.
+ */
+static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) {
+ int64_t pkt_num = pktns->tx.last_pkt_num + 1;
+ ngtcp2_rtb *rtb = &pktns->rtb;
+ int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num;
+
+ if (NGTCP2_MAX_PKT_NUM / 2 <= pkt_num) {
+ return 4;
+ }
+
+ n = n * 2 + 1;
+
+ if (n > 0xffffff) {
+ return 4;
+ }
+ if (n > 0xffff) {
+ return 3;
+ }
+ if (n > 0xff) {
+ return 2;
+ }
+ return 1;
+}
+
+/*
+ * 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;
+
+ return bytes_in_flight >= cwnd;
+}
+
+/*
+ * 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
+ */
+static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_strm *strm;
+
+ if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) {
+ return 0;
+ }
+
+ for (; !ngtcp2_pq_empty(&conn->tx.strmq);) {
+ strm = ngtcp2_conn_tx_strmq_top(conn);
+ if (ngtcp2_strm_streamfrq_empty(strm)) {
+ ngtcp2_conn_tx_strmq_pop(conn);
+ continue;
+ }
+
+ frc = ngtcp2_strm_streamfrq_top(strm);
+ return ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) +
+ NGTCP2_STREAM_OVERHEAD;
+ }
+
+ return 0;
+}
+
+static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_ksl_it it;
+
+ 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_ksl_clear(&pktns->crypto.tx.frq);
+}
+
+/*
+ * conn_cryptofrq_unacked_offset returns the CRYPTO frame offset by
+ * taking into account acknowledged offset. If there is no data to
+ * send, this function returns (uint64_t)-1.
+ */
+static uint64_t conn_cryptofrq_unacked_offset(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_crypto *fr;
+ ngtcp2_range gap;
+ ngtcp2_rtb *rtb = &pktns->rtb;
+ ngtcp2_ksl_it it;
+ size_t datalen;
+
+ (void)conn;
+
+ 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);
+ fr = &frc->fr.crypto;
+
+ gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->offset);
+
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (gap.begin <= fr->offset) {
+ return fr->offset;
+ }
+ if (gap.begin < fr->offset + datalen) {
+ return gap.begin;
+ }
+ }
+
+ return (uint64_t)-1;
+}
+
+static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_frame_chain **pfrc) {
+ ngtcp2_frame_chain *frc, *nfrc;
+ ngtcp2_crypto *fr, *nfr;
+ uint64_t offset, end_offset;
+ size_t idx, end_idx;
+ uint64_t base_offset, end_base_offset;
+ ngtcp2_range gap;
+ ngtcp2_rtb *rtb = &pktns->rtb;
+ ngtcp2_vec *v;
+ int rv;
+ ngtcp2_ksl_it it;
+
+ *pfrc = NULL;
+
+ for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);) {
+ frc = ngtcp2_ksl_it_get(&it);
+ fr = &frc->fr.crypto;
+
+ ngtcp2_ksl_remove(&pktns->crypto.tx.frq, &it, &fr->offset);
+
+ idx = 0;
+ offset = fr->offset;
+ base_offset = 0;
+
+ gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, offset);
+ if (gap.begin < offset) {
+ gap.begin = offset;
+ }
+
+ for (; idx < fr->datacnt && offset < gap.begin; ++idx) {
+ v = &fr->data[idx];
+ if (offset + v->len > gap.begin) {
+ base_offset = gap.begin - offset;
+ break;
+ }
+
+ offset += v->len;
+ }
+
+ if (idx == fr->datacnt) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ continue;
+ }
+
+ assert(gap.begin == offset + base_offset);
+
+ end_idx = idx;
+ end_offset = offset;
+ end_base_offset = 0;
+
+ for (; end_idx < fr->datacnt; ++end_idx) {
+ v = &fr->data[end_idx];
+ if (end_offset + v->len > gap.end) {
+ end_base_offset = gap.end - end_offset;
+ break;
+ }
+
+ end_offset += v->len;
+ }
+
+ if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) {
+ *pfrc = frc;
+ return 0;
+ }
+
+ if (fr->datacnt == end_idx) {
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx,
+ conn->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ nfr->type = NGTCP2_FRAME_CRYPTO;
+ memcpy(nfr->data, fr->data + end_idx,
+ sizeof(nfr->data[0]) * (fr->datacnt - end_idx));
+
+ assert(nfr->data[0].len > end_base_offset);
+
+ nfr->offset = end_offset + end_base_offset;
+ nfr->datacnt = fr->datacnt - end_idx;
+ nfr->data[0].base += end_base_offset;
+ nfr->data[0].len -= (size_t)end_base_offset;
+
+ 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);
+ return rv;
+ }
+
+ if (end_base_offset) {
+ ++end_idx;
+ }
+
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ if (end_base_offset) {
+ assert(fr->data[fr->datacnt - 1].len > end_base_offset);
+ fr->data[fr->datacnt - 1].len = (size_t)end_base_offset;
+ }
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ return 0;
+}
+static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
+ ngtcp2_pktns *pktns, size_t left) {
+ ngtcp2_crypto *fr, *nfr;
+ ngtcp2_frame_chain *frc, *nfrc;
+ int rv;
+ size_t nmerged;
+ size_t datalen;
+ ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT];
+ ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT];
+ size_t acnt, bcnt;
+ ngtcp2_ksl_it it;
+
+ rv = conn_cryptofrq_unacked_pop(conn, pktns, &frc);
+ if (rv != 0) {
+ return rv;
+ }
+ if (frc == NULL) {
+ *pfrc = NULL;
+ return 0;
+ }
+
+ fr = &frc->fr.crypto;
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (datalen > left) {
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ bcnt = 0;
+ ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_CRYPTO_DATACNT);
+
+ assert(acnt > 0);
+ assert(bcnt > 0);
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ nfr->type = NGTCP2_FRAME_CRYPTO;
+ nfr->offset = fr->offset + left;
+ nfr->datacnt = bcnt;
+ ngtcp2_vec_copy(nfr->data, b, bcnt);
+
+ 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);
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ *nfr = *fr;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_del(frc, conn->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+ }
+
+ left -= datalen;
+
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ for (; left && ngtcp2_ksl_len(&pktns->crypto.tx.frq);) {
+ it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq);
+ nfrc = ngtcp2_ksl_it_get(&it);
+ nfr = &nfrc->fr.crypto;
+
+ if (nfr->offset != fr->offset + datalen) {
+ assert(fr->offset + datalen < nfr->offset);
+ break;
+ }
+
+ rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+ if (nfrc == NULL) {
+ break;
+ }
+
+ nfr = &nfrc->fr.crypto;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left,
+ NGTCP2_MAX_CRYPTO_DATACNT);
+ if (nmerged == 0) {
+ 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);
+ return rv;
+ }
+ break;
+ }
+
+ datalen += nmerged;
+ left -= nmerged;
+
+ if (nfr->datacnt == 0) {
+ ngtcp2_frame_chain_del(nfrc, conn->mem);
+ continue;
+ }
+
+ nfr->offset += nmerged;
+
+ 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);
+ return rv;
+ }
+
+ break;
+ }
+
+ if (acnt == fr->datacnt) {
+ assert(acnt > 0);
+ fr->data[acnt - 1] = a[acnt - 1];
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ assert(acnt > fr->datacnt);
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ *nfr = *fr;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_del(frc, conn->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+}
+
+/*
+ * conn_verify_dcid verifies that destination connection ID in |hd| is
+ * valid for the connection. If it is successfully verified and the
+ * remote endpoint uses new DCID in the packet, nonzero value is
+ * assigned to |*pnew_cid_used| if it is not NULL. Otherwise 0 is
+ * assigned to it.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |dcid| is not known to the local endpoint.
+ */
+static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used,
+ const ngtcp2_pkt_hd *hd) {
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+ int rv;
+
+ it = ngtcp2_ksl_lower_bound(&conn->scid.set, &hd->dcid);
+ if (ngtcp2_ksl_it_end(&it)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ scid = ngtcp2_ksl_it_get(&it);
+ if (!ngtcp2_cid_eq(&scid->cid, &hd->dcid)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!(scid->flags & NGTCP2_SCID_FLAG_USED)) {
+ scid->flags |= NGTCP2_SCID_FLAG_USED;
+
+ if (scid->pe.index == NGTCP2_PQ_BAD_INDEX) {
+ rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (pnew_cid_used) {
+ *pnew_cid_used = 1;
+ }
+ } else if (pnew_cid_used) {
+ *pnew_cid_used = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+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;
+
+ if (conn->server) {
+ if (type != NGTCP2_PKT_INITIAL || !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;
+ } else {
+ return 1;
+ }
+ } 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) {
+ 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);
+ }
+ }
+
+ 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;
+}
+
+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;
+}
+
+static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ conn->idle_ts = 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_write_handshake_pkt writes handshake packet in the buffer
+ * pointed by |dest| whose length is |destlen|. |type| specifies long
+ * packet type. It should be either NGTCP2_PKT_INITIAL or
+ * NGTCP2_PKT_HANDSHAKE_PKT.
+ *
+ * This function returns the number of bytes written in |dest| 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 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) {
+ int rv;
+ ngtcp2_ppe ppe;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_frame_chain *frq = NULL, **pfrc = &frq;
+ ngtcp2_frame_chain *nfrc;
+ ngtcp2_frame *ackfr = NULL, lfr;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_cc cc;
+ ngtcp2_rtb_entry *rtbent;
+ ngtcp2_pktns *pktns;
+ size_t left;
+ uint8_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;
+
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ if (!conn->in_pktns) {
+ return 0;
+ }
+ assert(conn->in_pktns->crypto.tx.ckm);
+ pktns = conn->in_pktns;
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) {
+ return 0;
+ }
+ pktns = conn->hs_pktns;
+ break;
+ default:
+ assert(0);
+ }
+
+ 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,
+ &conn->dcid.current.cid, &conn->oscid,
+ pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
+ conn->version, 0);
+
+ if (!conn->server && type == NGTCP2_PKT_INITIAL &&
+ conn->local.settings.token.len) {
+ hd.token = conn->local.settings.token;
+ }
+
+ ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
+
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) {
+ return 0;
+ }
+
+ rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts,
+ /* ack_delay = */ 0,
+ NGTCP2_DEFAULT_ACK_DELAY_EXPONENT);
+ if (rv != 0) {
+ ngtcp2_frame_chain_list_del(frq, conn->mem);
+ return rv;
+ }
+
+ if (ackfr) {
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ ngtcp2_acktr_commit_ack(&pktns->acktr);
+ ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack);
+ pkt_empty = 0;
+ }
+ }
+
+ /* Server requires at least NGTCP2_DEFAULT_MAX_PKTLEN bytes in order
+ to send ack-eliciting Initial packet. */
+ if (!conn->server || type != NGTCP2_PKT_INITIAL ||
+ destlen >= NGTCP2_DEFAULT_MAX_PKTLEN) {
+ build_pkt:
+ for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) {
+ left = ngtcp2_ppe_left(&ppe);
+
+ crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns);
+ if (crypto_offset == (size_t)-1) {
+ conn_cryptofrq_clear(conn, pktns);
+ break;
+ }
+
+ left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left);
+ if (left == (size_t)-1) {
+ break;
+ }
+
+ 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);
+ return rv;
+ }
+
+ if (nfrc == NULL) {
+ break;
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr);
+ if (rv != 0) {
+ assert(0);
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_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);
+ if (num_reclaimed < 0) {
+ ngtcp2_frame_chain_list_del(frq, conn->mem);
+ return rv;
+ }
+ if (num_reclaimed) {
+ goto build_pkt;
+ }
+ /* We had pktns->rtb.num_retransmittable > 0 but the contents of
+ those packets have been acknowledged (i.e., retransmission in
+ another packet). For server, in this case, we don't have to
+ 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 &&
+ (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);
+ }
+ }
+
+ /* 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))) {
+ lfr.type = NGTCP2_FRAME_PING;
+
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
+ if (rv != 0) {
+ assert(rv == NGTCP2_ERR_NOBUF);
+ } else {
+ rtb_entry_flags |=
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PROBE;
+ pkt_empty = 0;
+ }
+ }
+
+ if (!pkt_empty) {
+ 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) {
+ lfr.type = NGTCP2_FRAME_PING;
+
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
+ if (rv != 0) {
+ assert(rv == NGTCP2_ERR_NOBUF);
+ } else {
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+ } else {
+ ++pktns->tx.num_non_ack_pkt;
+ }
+ } else {
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+ }
+ }
+
+ if (pkt_empty) {
+ return 0;
+ }
+
+ /* 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)) {
+ lfr.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+ } else {
+ lfr.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe);
+ }
+
+ if (lfr.padding.len) {
+ padded = 1;
+ ngtcp2_log_tx_fr(&conn->log, &hd, &lfr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
+ }
+
+ spktlen = ngtcp2_ppe_final(&ppe, NULL);
+ if (spktlen < 0) {
+ assert(ngtcp2_err_is_fatal((int)spktlen));
+ ngtcp2_frame_chain_list_del(frq, conn->mem);
+ return spktlen;
+ }
+
+ ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen);
+
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) {
+ if (pi) {
+ 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);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_list_del(frq, conn->mem);
+ return rv;
+ }
+
+ rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
+ if (rv != 0) {
+ ngtcp2_rtb_entry_del(rtbent, conn->mem);
+ return rv;
+ }
+
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) {
+ conn_restart_timer_on_write(conn, ts);
+ }
+ } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+ conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts);
+ }
+
+ if (pktns->rtb.probe_pkt_left &&
+ (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ --pktns->rtb.probe_pkt_left;
+ }
+
+ conn->dcid.current.bytes_sent += (uint64_t)spktlen;
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ ++pktns->tx.last_pkt_num;
+
+ return spktlen;
+}
+
+/*
+ * conn_write_ack_pkt writes QUIC packet for type |type| which only
+ * includes ACK frame in the buffer pointed by |dest| whose length is
+ * |destlen|.
+ *
+ * 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-defined callback function failed.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ uint8_t type, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_frame *ackfr;
+ ngtcp2_pktns *pktns;
+ ngtcp2_duration ack_delay;
+ uint64_t ack_delay_exponent;
+ ngtcp2_ssize spktlen;
+
+ assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING));
+
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ assert(conn->server);
+ pktns = conn->in_pktns;
+ ack_delay = 0;
+ ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ pktns = conn->hs_pktns;
+ ack_delay = 0;
+ ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ break;
+ case NGTCP2_PKT_SHORT:
+ pktns = &conn->pktns;
+ ack_delay = conn_compute_ack_delay(conn);
+ ack_delay_exponent = conn->local.transport_params.ack_delay_exponent;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (!pktns->crypto.tx.ckm) {
+ return 0;
+ }
+
+ ackfr = NULL;
+ rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts, ack_delay,
+ ack_delay_exponent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!ackfr) {
+ return 0;
+ }
+
+ spktlen = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, type, &conn->dcid.current.cid, ackfr,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+
+ if (spktlen <= 0) {
+ return spktlen;
+ }
+
+ conn->dcid.current.bytes_sent += (uint64_t)spktlen;
+
+ return spktlen;
+}
+
+static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns **ppktns,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pktns *pktns = *ppktns;
+ uint64_t bytes_in_flight;
+
+ bytes_in_flight = pktns->rtb.cc_bytes_in_flight;
+
+ assert(conn->cstat.bytes_in_flight >= bytes_in_flight);
+
+ conn->cstat.bytes_in_flight -= bytes_in_flight;
+ conn->cstat.pto_count = 0;
+ conn->cstat.last_tx_pkt_ts[pktns->rtb.pktns_id] = UINT64_MAX;
+ conn->cstat.loss_time[pktns->rtb.pktns_id] = UINT64_MAX;
+
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx);
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx);
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx);
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx);
+
+ pktns_del(pktns, conn->mem);
+ *ppktns = NULL;
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+}
+
+/*
+ * conn_discard_initial_state discards state for Initial packet number
+ * space.
+ */
+static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ if (!conn->in_pktns) {
+ return;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "discarding Initial packet number space");
+
+ conn_discard_pktns(conn, &conn->in_pktns, ts);
+}
+
+/*
+ * conn_discard_handshake_state discards state for Handshake packet
+ * number space.
+ */
+static void conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ if (!conn->hs_pktns) {
+ return;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "discarding Handshake packet number space");
+
+ conn_discard_pktns(conn, &conn->hs_pktns, ts);
+}
+
+/*
+ * conn_discard_early_key discards early key.
+ */
+static void conn_discard_early_key(ngtcp2_conn *conn) {
+ assert(conn->early.ckm);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "discarding early key");
+
+ conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx);
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx);
+ memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx));
+
+ ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
+ conn->early.ckm = NULL;
+}
+
+/*
+ * conn_write_handshake_ack_pkts writes packets which contain ACK
+ * frame only. This function writes at most 2 packets for each
+ * Initial and Handshake packet.
+ */
+static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize res = 0, nwrite = 0;
+
+ /* In the most cases, client sends ACK in conn_write_handshake_pkt.
+ This function is only called when it is CWND limited. It is not
+ required for client to send ACK for server Initial. This is
+ because once it gets server Initial, it gets Handshake tx key and
+ discards Initial key. The only good reason to send ACK is give
+ server RTT measurement early. */
+ if (conn->server && conn->in_pktns) {
+ nwrite =
+ conn_write_ack_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, ts);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ res += nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+
+ if (conn->hs_pktns->crypto.tx.ckm) {
+ nwrite =
+ conn_write_ack_pkt(conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, ts);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (!conn->server && nwrite) {
+ conn_discard_initial_state(conn, ts);
+ }
+ }
+
+ return res;
+}
+
+/*
+ * conn_write_client_initial writes Initial packet in the buffer
+ * pointed by |dest| whose length is |destlen|.
+ *
+ * This function returns the number of bytes written in |dest| 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 ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ size_t early_datalen,
+ ngtcp2_tstamp ts) {
+ 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 conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
+ NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen,
+ ts);
+}
+
+/*
+ * conn_write_handshake_pkts writes Initial and Handshake packets in
+ * the buffer pointed by |dest| whose length is |destlen|.
+ *
+ * This function returns the number of bytes written in |dest| 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 ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ size_t early_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_ksl_it it;
+
+ /* As a client, we would like to discard Initial packet number space
+ when sending the first Handshake packet. When sending Handshake
+ packet, it should be one of 1) sending ACK, 2) sending PTO probe
+ packet, or 3) sending CRYPTO. If we have pending acknowledgement
+ for Initial, then do not discard Initial packet number space.
+ Otherwise, if either 1) or 2) is satisfied, discard Initial
+ packet number space. When sending Handshake CRYPTO, it indicates
+ that client has received Handshake CRYPTO from server. Initial
+ packet number space is discarded because 1) is met. If there is
+ pending Initial ACK, Initial packet number space is discarded
+ after writing the first Handshake packet.
+ */
+ if (!conn->server && conn->hs_pktns->crypto.tx.ckm && conn->in_pktns &&
+ !ngtcp2_acktr_require_active_ack(&conn->in_pktns->acktr,
+ /* max_ack_delay = */ 0, ts) &&
+ (ngtcp2_acktr_require_active_ack(&conn->hs_pktns->acktr,
+ /* max_ack_delay = */ 0, ts) ||
+ conn->hs_pktns->rtb.probe_pkt_left)) {
+ /* Discard Initial state here so that Handshake packet is not
+ 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);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ if (nwrite == 0) {
+ if (conn->server && (conn->in_pktns->rtb.probe_pkt_left ||
+ ngtcp2_ksl_len(&conn->in_pktns->crypto.tx.frq))) {
+ return 0;
+ }
+ } else {
+ res += nwrite;
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+ nwrite = conn_write_handshake_pkt(conn, pi, dest, destlen,
+ NGTCP2_PKT_HANDSHAKE, wflags, 0, ts);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (!conn->server && conn->hs_pktns->crypto.tx.ckm && nwrite) {
+ /* We don't need to send further Initial packet if we have
+ Handshake key and sent something with it. So discard initial
+ state here. */
+ conn_discard_initial_state(conn, ts);
+ }
+
+ return res;
+}
+
+/*
+ * conn_initial_stream_rx_offset returns the initial maximum offset of
+ * data for a stream denoted by |stream_id|.
+ */
+static uint64_t conn_initial_stream_rx_offset(ngtcp2_conn *conn,
+ int64_t stream_id) {
+ int local_stream = conn_local_stream(conn, stream_id);
+
+ if (bidi_stream(stream_id)) {
+ if (local_stream) {
+ return conn->local.transport_params.initial_max_stream_data_bidi_local;
+ }
+ return conn->local.transport_params.initial_max_stream_data_bidi_remote;
+ }
+
+ if (local_stream) {
+ return 0;
+ }
+ return conn->local.transport_params.initial_max_stream_data_uni;
+}
+
+/*
+ * conn_should_send_max_stream_data returns nonzero if MAX_STREAM_DATA
+ * frame should be send for |strm|.
+ */
+static int conn_should_send_max_stream_data(ngtcp2_conn *conn,
+ ngtcp2_strm *strm) {
+ uint64_t inc = strm->rx.unsent_max_offset - strm->rx.max_offset;
+ (void)conn;
+
+ return strm->rx.window < 2 * inc;
+}
+
+/*
+ * conn_should_send_max_data returns nonzero if MAX_DATA frame should
+ * be sent.
+ */
+static int conn_should_send_max_data(ngtcp2_conn *conn) {
+ uint64_t inc = conn->rx.unsent_max_offset - conn->rx.max_offset;
+
+ return conn->rx.window < 2 * inc;
+}
+
+/*
+ * conn_required_num_new_connection_id returns the number of
+ * additional connection ID the local endpoint has to provide to the
+ * remote endpoint.
+ */
+static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) {
+ uint64_t n;
+ size_t len = ngtcp2_ksl_len(&conn->scid.set);
+
+ if (len >= NGTCP2_MAX_SCID_POOL_SIZE) {
+ return 0;
+ }
+
+ 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 +
+ conn->scid.num_retired;
+
+ return (size_t)ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len;
+}
+
+/*
+ * conn_enqueue_new_connection_id generates additional connection IDs
+ * and prepares to send them to the remote endpoint.
+ *
+ * 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_enqueue_new_connection_id(ngtcp2_conn *conn) {
+ size_t i, need = conn_required_num_new_connection_id(conn);
+ size_t cidlen = conn->oscid.datalen;
+ ngtcp2_cid cid;
+ uint64_t seq;
+ int rv;
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ ngtcp2_frame_chain *nfrc;
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_scid *scid;
+ ngtcp2_ksl_it it;
+
+ for (i = 0; i < need; ++i) {
+ rv = conn_call_get_new_connection_id(conn, &cid, token, cidlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (cid.datalen != cidlen) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ /* Assert uniqueness */
+ it = ngtcp2_ksl_lower_bound(&conn->scid.set, &cid);
+ if (!ngtcp2_ksl_it_end(&it) &&
+ ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it), &cid)) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ seq = ++conn->scid.last_seq;
+
+ scid = ngtcp2_mem_malloc(conn->mem, sizeof(*scid));
+ if (scid == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_scid_init(scid, seq, &cid, token);
+
+ rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid);
+ if (rv != 0) {
+ ngtcp2_mem_free(conn->mem, scid);
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ nfrc->fr.new_connection_id.seq = seq;
+ nfrc->fr.new_connection_id.retire_prior_to = 0;
+ nfrc->fr.new_connection_id.cid = cid;
+ memcpy(nfrc->fr.new_connection_id.stateless_reset_token, token,
+ sizeof(token));
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_remove_retired_connection_id removes the already retired
+ * connection ID. It waits PTO before actually removing a connection
+ * ID after it receives RETIRE_CONNECTION_ID from peer to catch
+ * reordered packets.
+ *
+ * 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_remove_retired_connection_id(ngtcp2_conn *conn,
+ ngtcp2_duration pto,
+ ngtcp2_tstamp ts) {
+ ngtcp2_duration timeout = pto;
+ ngtcp2_scid *scid;
+ ngtcp2_dcid *dcid;
+ int rv;
+
+ 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) {
+ break;
+ }
+
+ assert(scid->flags & NGTCP2_SCID_FLAG_RETIRED);
+
+ rv = conn_call_remove_connection_id(conn, &scid->cid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ksl_remove(&conn->scid.set, NULL, &scid->cid);
+ ngtcp2_pq_pop(&conn->scid.used);
+ ngtcp2_mem_free(conn->mem, scid);
+
+ assert(conn->scid.num_retired);
+ --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) {
+ break;
+ }
+
+ rv = conn_call_deactivate_dcid(conn, dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.retired);
+ }
+
+ return 0;
+}
+
+/*
+ * conn_min_short_pktlen returns the minimum length of Short packet
+ * this endpoint sends.
+ */
+static size_t conn_min_short_pktlen(ngtcp2_conn *conn) {
+ return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN;
+}
+
+/*
+ * 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.
+ *
+ * This function can send new stream data. In order to send stream
+ * data, specify the underlying stream and parameters to
+ * |vmsg|->stream. If |vmsg|->stream.fin is set to nonzero, it
+ * signals that the given data is the final portion of the stream.
+ * |vmsg|->stream.data vector of length |vmsg|->stream.datacnt
+ * specifies stream data to send. The number of bytes sent to the
+ * stream is assigned to *|vmsg|->stream.pdatalen. If 0 length STREAM
+ * data is sent, 0 is assigned to it. The caller should initialize
+ * *|vmsg|->stream.pdatalen to -1.
+ *
+ * If |require_padding| is nonzero, padding bytes are added to occupy
+ * the remaining packet payload.
+ *
+ * This function returns the number of bytes written in |dest| 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.
+ * NGTCP2_ERR_STREAM_DATA_BLOCKED
+ * Stream data could not be written because of flow control.
+ */
+static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg, uint8_t type,
+ uint8_t flags, ngtcp2_tstamp ts) {
+ int rv = 0;
+ ngtcp2_crypto_cc *cc = &conn->pkt.cc;
+ ngtcp2_ppe *ppe = &conn->pkt.ppe;
+ ngtcp2_pkt_hd *hd = &conn->pkt.hd;
+ ngtcp2_frame *ackfr = NULL, lfr;
+ ngtcp2_ssize nwrite;
+ ngtcp2_frame_chain **pfrc, *nfrc, *frc;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_strm *strm;
+ int pkt_empty = 1;
+ size_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;
+ ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
+ size_t datacnt;
+ uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
+ int hd_logged = 0;
+ ngtcp2_path_challenge_entry *pcent;
+ uint8_t hd_flags;
+ 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;
+ ngtcp2_ssize num_reclaimed;
+ int fin;
+ uint64_t target_max_data;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ uint64_t delta;
+
+ /* Return 0 if destlen is less than minimum packet length which can
+ trigger Stateless Reset */
+ if (destlen < min_pktlen) {
+ return 0;
+ }
+
+ if (vmsg) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt);
+ ndatalen = conn_enforce_flow_control(conn, vmsg->stream.strm, datalen);
+ /* 0 length STREAM frame is allowed */
+ if (ndatalen || datalen == 0) {
+ send_stream = 1;
+ } else {
+ stream_blocked = 1;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt);
+ send_datagram = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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;
+ 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;
+
+ /* 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)) {
+ rv = conn_enqueue_new_connection_id(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ break;
+ case NGTCP2_PKT_0RTT:
+ assert(!conn->server);
+ if (!conn->early.ckm) {
+ return 0;
+ }
+ hd_flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ cc->aead = conn->early.ctx.aead;
+ cc->hp = conn->early.ctx.hp;
+ cc->ckm = conn->early.ckm;
+ cc->hp_ctx = conn->early.hp_ctx;
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+
+ cc->encrypt = conn->callbacks.encrypt;
+ cc->hp_mask = conn->callbacks.hp_mask;
+
+ if (conn_should_send_max_data(conn)) {
+ rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->local.settings.max_window &&
+ conn->tx.last_max_data_ts != UINT64_MAX &&
+ ts - conn->tx.last_max_data_ts <
+ NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt &&
+ conn->local.settings.max_window > conn->rx.window) {
+ target_max_data = NGTCP2_FLOW_WINDOW_SCALING_FACTOR * conn->rx.window;
+ if (target_max_data > conn->local.settings.max_window) {
+ target_max_data = conn->local.settings.max_window;
+ }
+
+ delta = target_max_data - conn->rx.window;
+ if (conn->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) {
+ delta = NGTCP2_MAX_VARINT - conn->rx.unsent_max_offset;
+ }
+
+ conn->rx.window = target_max_data;
+ } else {
+ delta = 0;
+ }
+
+ conn->tx.last_max_data_ts = ts;
+
+ nfrc->fr.type = NGTCP2_FRAME_MAX_DATA;
+ nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset + delta;
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+
+ 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_ppe_init(ppe, dest, destlen, cc);
+
+ rv = ngtcp2_ppe_encode_hd(ppe, hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(ppe)) {
+ return 0;
+ }
+
+ if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) {
+ pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0);
+
+ /* PATH_RESPONSE is bound to the path that the corresponding
+ PATH_CHALLENGE is received. */
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) {
+ lfr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ memcpy(lfr.path_response.data, pcent->data,
+ sizeof(lfr.path_response.data));
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ require_padding = !conn->server || destlen >= 1200;
+ /* We don't retransmit PATH_RESPONSE. */
+ }
+ }
+ }
+
+ rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts,
+ conn_compute_ack_delay(conn),
+ 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);
+ pkt_empty = 0;
+ }
+ }
+
+ build_pkt:
+ for (pfrc = &pktns->tx.frq; *pfrc;) {
+ if ((*pfrc)->binder &&
+ ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ continue;
+ }
+
+ switch ((*pfrc)->fr.type) {
+ case NGTCP2_FRAME_STOP_SENDING:
+ strm =
+ ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id);
+ if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_STREAM:
+ assert(0);
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ if ((*pfrc)->fr.max_streams.max_streams <
+ conn->remote.bidi.max_streams) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ if ((*pfrc)->fr.max_streams.max_streams <
+ conn->remote.uni.max_streams) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ strm = ngtcp2_conn_find_stream(conn,
+ (*pfrc)->fr.max_stream_data.stream_id);
+ if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) ||
+ (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_MAX_DATA:
+ if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ ++conn->dcid.num_retire_queued;
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ assert(0);
+ break;
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ break;
+ }
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF) {
+ for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) {
+ left = ngtcp2_ppe_left(ppe);
+
+ crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns);
+ if (crypto_offset == (size_t)-1) {
+ conn_cryptofrq_clear(conn, pktns);
+ break;
+ }
+
+ left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left);
+
+ if (left == (size_t)-1) {
+ break;
+ }
+
+ rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (nfrc == NULL) {
+ break;
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ assert(0);
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ }
+ }
+
+ /* Write MAX_STREAM_ID after RESET_STREAM so that we can extend stream
+ ID space in one packet. */
+ if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL &&
+ conn->remote.bidi.unsent_max_streams > conn->remote.bidi.max_streams) {
+ rv = conn_call_extend_max_remote_streams_bidi(
+ conn, conn->remote.bidi.unsent_max_streams);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI;
+ nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams;
+ *pfrc = nfrc;
+
+ conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ }
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL) {
+ if (conn->remote.uni.unsent_max_streams > conn->remote.uni.max_streams) {
+ rv = conn_call_extend_max_remote_streams_uni(
+ conn, conn->remote.uni.unsent_max_streams);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI;
+ nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams;
+ *pfrc = nfrc;
+
+ conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd,
+ &(*pfrc)->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ }
+ }
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF) {
+ for (; !ngtcp2_pq_empty(&conn->tx.strmq);) {
+ strm = ngtcp2_conn_tx_strmq_top(conn);
+
+ if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ conn_should_send_max_stream_data(conn, strm)) {
+ rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (conn->local.settings.max_stream_window &&
+ strm->tx.last_max_stream_data_ts != UINT64_MAX &&
+ ts - strm->tx.last_max_stream_data_ts <
+ NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt &&
+ conn->local.settings.max_stream_window > strm->rx.window) {
+ target_max_data =
+ NGTCP2_FLOW_WINDOW_SCALING_FACTOR * strm->rx.window;
+ if (target_max_data > conn->local.settings.max_stream_window) {
+ target_max_data = conn->local.settings.max_stream_window;
+ }
+
+ delta = target_max_data - strm->rx.window;
+ if (strm->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) {
+ delta = NGTCP2_MAX_VARINT - strm->rx.unsent_max_offset;
+ }
+
+ strm->rx.window = target_max_data;
+ } else {
+ delta = 0;
+ }
+
+ strm->tx.last_max_stream_data_ts = ts;
+
+ nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ nfrc->fr.max_stream_data.stream_id = strm->stream_id;
+ nfrc->fr.max_stream_data.max_stream_data =
+ strm->rx.unsent_max_offset + delta;
+ ngtcp2_list_insert(nfrc, pfrc);
+
+ rv =
+ conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ break;
+ }
+
+ pkt_empty = 0;
+ credit_expanded = 1;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ strm->rx.max_offset = strm->rx.unsent_max_offset =
+ nfrc->fr.max_stream_data.max_stream_data;
+ }
+
+ if (ngtcp2_strm_streamfrq_empty(strm)) {
+ ngtcp2_conn_tx_strmq_pop(conn);
+ continue;
+ }
+
+ stream_offset = ngtcp2_strm_streamfrq_unacked_offset(strm);
+ if (stream_offset == (uint64_t)-1) {
+ ngtcp2_strm_streamfrq_clear(strm);
+ ngtcp2_conn_tx_strmq_pop(conn);
+ continue;
+ }
+
+ left = ngtcp2_ppe_left(ppe);
+
+ left = ngtcp2_pkt_stream_max_datalen(strm->stream_id, stream_offset,
+ left, left);
+
+ if (left == (size_t)-1) {
+ break;
+ }
+
+ rv = ngtcp2_strm_streamfrq_pop(strm, &nfrc, left);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (nfrc == NULL) {
+ /* TODO Why? */
+ break;
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ assert(0);
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+
+ if (ngtcp2_strm_streamfrq_empty(strm)) {
+ ngtcp2_conn_tx_strmq_pop(conn);
+ continue;
+ }
+
+ ngtcp2_conn_tx_strmq_pop(conn);
+ ++strm->cycle;
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ }
+ }
+
+ /* 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);
+ if (num_reclaimed < 0) {
+ return rv;
+ }
+ if (num_reclaimed) {
+ goto build_pkt;
+ }
+
+ /* We had pktns->rtb.num_retransmittable > 0 but the contents of
+ 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) {
+ pktns->rtb.probe_pkt_left = 0;
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ }
+ }
+ } else {
+ pfrc = conn->pkt.pfrc;
+ rtb_entry_flags |= conn->pkt.rtb_entry_flags;
+ pkt_empty = conn->pkt.pkt_empty;
+ hd_logged = conn->pkt.hd_logged;
+ }
+
+ left = ngtcp2_ppe_left(ppe);
+
+ if (rv != NGTCP2_ERR_NOBUF && send_stream && *pfrc == NULL &&
+ (ndatalen = ngtcp2_pkt_stream_max_datalen(
+ 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);
+
+ rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ nfrc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ nfrc->fr.stream.flags = 0;
+ nfrc->fr.stream.stream_id = vmsg->stream.strm->stream_id;
+ nfrc->fr.stream.offset = vmsg->stream.strm->tx.offset;
+ nfrc->fr.stream.datacnt = datacnt;
+ ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt);
+
+ fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
+ ndatalen == datalen;
+ nfrc->fr.stream.fin = (uint8_t)fin;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ assert(0);
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+
+ vmsg->stream.strm->tx.offset += ndatalen;
+ conn->tx.offset += ndatalen;
+
+ if (fin) {
+ ngtcp2_strm_shutdown(vmsg->stream.strm, NGTCP2_STRM_FLAG_SHUT_WR);
+ }
+
+ if (vmsg->stream.pdatalen) {
+ *vmsg->stream.pdatalen = (ngtcp2_ssize)ndatalen;
+ }
+ } else {
+ send_stream = 0;
+ }
+
+ 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;
+
+ 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;
+
+ if (vmsg->datagram.paccepted) {
+ *vmsg->datagram.paccepted = 1;
+ }
+ } else {
+ send_datagram = 0;
+ }
+
+ if (pkt_empty) {
+ assert(rv == 0 || NGTCP2_ERR_NOBUF == rv);
+ if (rv == 0 && stream_blocked && ngtcp2_conn_get_max_data_left(conn)) {
+ return NGTCP2_ERR_STREAM_DATA_BLOCKED;
+ }
+
+ if (conn->pktns.rtb.probe_pkt_left == 0) {
+ return 0;
+ }
+ } else if (write_more) {
+ conn->pkt.pfrc = pfrc;
+ conn->pkt.pkt_empty = pkt_empty;
+ conn->pkt.rtb_entry_flags = rtb_entry_flags;
+ conn->pkt.hd_logged = hd_logged;
+ conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING;
+
+ assert(vmsg);
+
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ if (send_stream) {
+ if (ngtcp2_ppe_left(ppe)) {
+ return NGTCP2_ERR_WRITE_MORE;
+ }
+ } else if (ngtcp2_conn_get_max_data_left(conn) && stream_blocked) {
+ return NGTCP2_ERR_STREAM_DATA_BLOCKED;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ if (send_datagram && ngtcp2_ppe_left(ppe)) {
+ return NGTCP2_ERR_WRITE_MORE;
+ }
+ /* If DATAGRAM cannot be written due to insufficient space,
+ continue to create a packet with the hope that application
+ calls ngtcp2_conn_writev_datagram again. */
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) {
+ lfr.type = NGTCP2_FRAME_PING;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
+ if (rv != 0) {
+ assert(rv == NGTCP2_ERR_NOBUF);
+ /* TODO If buffer is too small, PING cannot be written if
+ packet is still empty. */
+ } else {
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+ } else {
+ ++pktns->tx.num_non_ack_pkt;
+ }
+ } else {
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+
+ /* 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)) {
+ lfr.padding.len = ngtcp2_ppe_padding(ppe);
+ } else {
+ lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen);
+ }
+
+ if (lfr.padding.len) {
+ padded = 1;
+ ngtcp2_log_tx_fr(&conn->log, hd, &lfr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
+ }
+
+ nwrite = ngtcp2_ppe_final(ppe, NULL);
+ if (nwrite < 0) {
+ assert(ngtcp2_err_is_fatal((int)nwrite));
+ return nwrite;
+ }
+
+ ++cc->ckm->use_count;
+
+ ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite);
+
+ /* TODO ack-eliciting vs needs-tracking */
+ /* probe packet needs tracking but it does not need ACK, could be lost. */
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) {
+ if (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);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal((int)nwrite));
+ return rv;
+ }
+
+ if (*pfrc != pktns->tx.frq) {
+ ent->frc = pktns->tx.frq;
+ pktns->tx.frq = *pfrc;
+ *pfrc = NULL;
+ }
+
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ pktns->rtb.num_ack_eliciting == 0 && conn->cc.event) {
+ conn->cc.event(&conn->cc, &conn->cstat, NGTCP2_CC_EVENT_TYPE_TX_START,
+ ts);
+ }
+
+ 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);
+ return rv;
+ }
+
+ if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ if (conn->cc.on_pkt_sent) {
+ 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));
+ }
+
+ if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) {
+ conn_restart_timer_on_write(conn, ts);
+ }
+ }
+ } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+ conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts);
+ }
+
+ conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING;
+
+ if (pktns->rtb.probe_pkt_left &&
+ (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ --pktns->rtb.probe_pkt_left;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
+ nwrite);
+ }
+
+ conn->dcid.current.bytes_sent += (uint64_t)nwrite;
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ ++pktns->tx.last_pkt_num;
+
+ return nwrite;
+}
+
+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) {
+ int rv;
+ ngtcp2_ppe ppe;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_frame lfr;
+ ngtcp2_ssize nwrite;
+ ngtcp2_crypto_cc cc;
+ ngtcp2_pktns *pktns;
+ uint8_t flags;
+ ngtcp2_rtb_entry *rtbent;
+ int padded = 0;
+
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ pktns = conn->in_pktns;
+ flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ pktns = conn->hs_pktns;
+ flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ break;
+ case NGTCP2_PKT_SHORT:
+ /* 0 means Short packet. */
+ 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;
+ break;
+ default:
+ /* We don't support 0-RTT packet in this function. */
+ assert(0);
+ }
+
+ 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,
+ pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
+ conn->version, 0);
+
+ ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
+
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) {
+ return 0;
+ }
+
+ ngtcp2_log_tx_pkt_hd(&conn->log, &hd);
+ ngtcp2_qlog_pkt_sent_start(&conn->qlog);
+
+ rv = conn_ppe_write_frame(conn, &ppe, &hd, fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ 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 (lfr.padding.len) {
+ padded = 1;
+ ngtcp2_log_tx_fr(&conn->log, &hd, &lfr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
+ }
+
+ nwrite = ngtcp2_ppe_final(&ppe, NULL);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (type == NGTCP2_PKT_SHORT) {
+ ++cc.ckm->use_count;
+ }
+
+ ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite);
+
+ /* Do this when we are sure that there is no error. */
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ ngtcp2_acktr_commit_ack(&pktns->acktr);
+ ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack);
+ break;
+ }
+
+ if (((rtb_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);
+ }
+
+ rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
+ if (rv != 0) {
+ ngtcp2_rtb_entry_del(rtbent, 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);
+ }
+ } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+ conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts);
+ }
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ ++pktns->tx.last_pkt_num;
+
+ return nwrite;
+}
+
+/*
+ * conn_process_early_rtb makes any pending 0RTT packet Short packet.
+ */
+static void conn_process_early_rtb(ngtcp2_conn *conn) {
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_rtb *rtb = &conn->pktns.rtb;
+ ngtcp2_ksl_it it;
+
+ for (it = ngtcp2_rtb_head(rtb); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if ((ent->hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) == 0 ||
+ ent->hd.type != NGTCP2_PKT_0RTT) {
+ continue;
+ }
+
+ /* 0-RTT packet is retransmitted as a Short packet. */
+ ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM;
+ ent->hd.type = NGTCP2_PKT_SHORT;
+ }
+}
+
+/*
+ * conn_handshake_remnants_left returns nonzero if there may be
+ * handshake packets the local endpoint has to send, including new
+ * packets and lost ones.
+ */
+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 ||
+ ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) ||
+ (hs_pktns && (hs_pktns->rtb.num_retransmittable ||
+ ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq)));
+}
+
+/*
+ * conn_retire_dcid_seq retires destination connection ID denoted by
+ * |seq|.
+ *
+ * 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_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);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+ nfrc->fr.retire_connection_id.seq = seq;
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+
+ return 0;
+}
+
+/*
+ * conn_retire_dcid retires |dcid|.
+ *
+ * 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_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ringbuf *rb = &conn->dcid.retired;
+ ngtcp2_dcid *dest, *stale_dcid;
+ int rv;
+
+ assert(dcid->cid.datalen);
+
+ if (ngtcp2_ringbuf_full(rb)) {
+ stale_dcid = ngtcp2_ringbuf_get(rb, 0);
+ rv = conn_call_deactivate_dcid(conn, stale_dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(rb);
+ }
+
+ dest = ngtcp2_ringbuf_push_back(rb);
+ ngtcp2_dcid_copy(dest, dcid);
+ dest->ts_retired = ts;
+
+ return conn_retire_dcid_seq(conn, dcid->seq);
+}
+
+/*
+ * conn_bind_dcid stores the DCID to |*pdcid| bound to |path|. If
+ * such DCID is not found, bind the new DCID to |path| and stores it
+ * to |*pdcid|. If a remote endpoint uses zero-length connection ID,
+ * the pointer to conn->dcid.current is assigned to |*pdcid|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONN_ID_BLOCKED
+ * No unused DCID is available
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+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));
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+
+ if (ngtcp2_path_eq(&dcid->ps.path, path)) {
+ *pdcid = dcid;
+ return 0;
+ }
+ }
+
+ if (conn->dcid.current.cid.datalen == 0) {
+ ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound);
+ ngtcp2_cid_zero(&cid);
+ ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL);
+ ngtcp2_path_copy(&ndcid->ps.path, path);
+
+ *pdcid = ndcid;
+
+ return 0;
+ }
+
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+ return NGTCP2_ERR_CONN_ID_BLOCKED;
+ }
+
+ if (ngtcp2_ringbuf_full(&conn->dcid.bound)) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 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);
+
+ ngtcp2_dcid_copy(ndcid, dcid);
+ ngtcp2_path_copy(&ndcid->ps.path, path);
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+
+ *pdcid = ndcid;
+
+ return 0;
+}
+
+/*
+ * conn_stop_pv stops the path validation which is currently running.
+ * This function does nothing if no path validation is currently being
+ * performed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv = 0;
+ ngtcp2_pv *pv = conn->pv;
+
+ if (pv == NULL) {
+ return 0;
+ }
+
+ if (pv->dcid.cid.datalen && pv->dcid.seq != conn->dcid.current.seq) {
+ rv = conn_retire_dcid(conn, &pv->dcid, ts);
+ if (rv != 0) {
+ goto fin;
+ }
+ }
+
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.cid.datalen &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq != pv->dcid.seq) {
+ rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts);
+ if (rv != 0) {
+ goto fin;
+ }
+ }
+
+fin:
+ ngtcp2_pv_del(pv);
+ conn->pv = NULL;
+
+ return rv;
+}
+
+static void conn_reset_congestion_state(ngtcp2_conn *conn);
+
+/*
+ * conn_on_path_validation_failed is called when path validation
+ * fails. This function may delete |pv|.
+ *
+ * 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_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_MTU_PROBE) {
+ return NGTCP2_ERR_NO_VIABLE_PATH;
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
+ ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid);
+ conn_reset_congestion_state(conn);
+ }
+
+ 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|.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ 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;
+
+ 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);
+ }
+
+ ngtcp2_pv_handle_entry_expiry(pv, ts);
+
+ if (!ngtcp2_pv_should_send_probe(pv)) {
+ 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);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ lfr.type = NGTCP2_FRAME_PATH_CHALLENGE;
+
+ timeout = conn_compute_pto(conn, &conn->pktns);
+ timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt);
+ expiry = ts + timeout * (1ULL << pv->round);
+
+ 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);
+ if (destlen == 0) {
+ return 0;
+ }
+ }
+
+ if (destlen < 1200) {
+ flags = NGTCP2_PV_ENTRY_FLAG_UNDERSIZED;
+ } else {
+ flags = NGTCP2_PV_ENTRY_FLAG_NONE;
+ }
+ } else {
+ flags = NGTCP2_PV_ENTRY_FLAG_NONE;
+ }
+
+ 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);
+ if (nwrite <= 0) {
+ return nwrite;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &pv->dcid.ps.path);
+ }
+
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) {
+ conn->dcid.current.bytes_sent += (uint64_t)nwrite;
+ } else {
+ pv->dcid.bytes_sent += (uint64_t)nwrite;
+ }
+
+ return nwrite;
+}
+
+/*
+ * conn_write_path_response writes a packet which includes
+ * PATH_RESPONSE frame into |dest| of length |destlen|.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_path_challenge_entry *pcent = NULL;
+ ngtcp2_dcid *dcid = NULL;
+ ngtcp2_frame lfr;
+ ngtcp2_ssize nwrite;
+ int rv;
+ size_t tx_left;
+
+ for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) {
+ pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0);
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) {
+ /* Send PATH_RESPONSE from conn_write_pkt. */
+ return 0;
+ }
+
+ if (pv) {
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, &pcent->ps.path)) {
+ dcid = &pv->dcid;
+ break;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ ngtcp2_path_eq(&pv->fallback_dcid.ps.path, &pcent->ps.path)) {
+ dcid = &pv->fallback_dcid;
+ break;
+ }
+ }
+
+ if (conn->server) {
+ break;
+ }
+
+ /* Client does not expect to respond to path validation against
+ unknown path */
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+ pcent = NULL;
+ }
+
+ if (pcent == NULL) {
+ return 0;
+ }
+
+ if (dcid == NULL) {
+ /* client is expected to have |path| in conn->dcid.current or
+ conn->pv. */
+ assert(conn->server);
+
+ rv = conn_bind_dcid(conn, &dcid, &pcent->ps.path, ts);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ return 0;
+ }
+ }
+
+ if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
+ tx_left = conn_server_tx_left(conn, dcid);
+ destlen = ngtcp2_min(destlen, tx_left);
+ if (destlen == 0) {
+ return 0;
+ }
+ }
+
+ lfr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ 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);
+ if (nwrite <= 0) {
+ return nwrite;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &pcent->ps.path);
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+
+ 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);
+}
+
+/*
+ * conn_on_version_negotiation is called when Version Negotiation
+ * packet is received. The function decodes the data in the buffer
+ * pointed by |payload| whose length is |payloadlen| as Version
+ * Negotiation packet payload. The packet header is given in |hd|.
+ *
+ * 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.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Packet payload is badly formatted.
+ */
+static int conn_on_version_negotiation(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ uint32_t sv[16];
+ uint32_t *p;
+ int rv = 0;
+ size_t nsv;
+ size_t i;
+
+ if (payloadlen % sizeof(uint32_t)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (payloadlen > sizeof(sv)) {
+ p = ngtcp2_mem_malloc(conn->mem, payloadlen);
+ if (p == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ } else {
+ p = sv;
+ }
+
+ nsv = ngtcp2_pkt_decode_version_negotiation(p, payload, payloadlen);
+
+ 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");
+
+ rv = NGTCP2_ERR_INVALID_ARGUMENT;
+ goto fin;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+fin:
+ if (p != sv) {
+ ngtcp2_mem_free(conn->mem, p);
+ }
+
+ return rv;
+}
+
+static uint64_t conn_tx_strmq_first_cycle(ngtcp2_conn *conn) {
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_pq_empty(&conn->tx.strmq)) {
+ return 0;
+ }
+
+ strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe);
+ return strm->cycle;
+}
+
+uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn) {
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_pq_empty(&conn->tx.strmq)) {
+ return 0;
+ }
+
+ strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe);
+ return strm->cycle;
+}
+
+int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_frame_chain **pfrc) {
+ ngtcp2_frame_chain **first = pfrc;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_stream *sfr;
+ ngtcp2_strm *strm;
+ int rv;
+
+ if (*pfrc == NULL) {
+ return 0;
+ }
+
+ for (; *pfrc;) {
+ switch ((*pfrc)->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+ sfr = &frc->fr.stream;
+
+ strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
+ if (!strm) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ break;
+ }
+ rv = ngtcp2_strm_streamfrq_push(strm, frc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+ if (!ngtcp2_strm_is_tx_queued(strm)) {
+ strm->cycle = conn_tx_strmq_first_cycle(conn);
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL,
+ &frc->fr.crypto.offset, frc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+ break;
+ default:
+ pfrc = &(*pfrc)->next;
+ }
+ }
+
+ *pfrc = pktns->tx.frq;
+ pktns->tx.frq = *first;
+
+ return 0;
+}
+
+/*
+ * conn_on_retry is called when Retry packet is received. The
+ * function decodes the data in the buffer pointed by |pkt| whose
+ * length is |pktlen| as Retry packet. The length of long packet
+ * header is given in |hdpktlen|. |pkt| includes packet header.
+ *
+ * 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.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Packet payload is badly formatted.
+ * NGTCP2_ERR_PROTO
+ * 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) {
+ int rv;
+ ngtcp2_pkt_retry retry;
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_rtb *rtb = &conn->pktns.rtb;
+ ngtcp2_rtb *in_rtb;
+ uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1];
+ ngtcp2_vec *token;
+
+ if (!in_pktns || conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) {
+ return 0;
+ }
+
+ in_rtb = &in_pktns->rtb;
+
+ rv = ngtcp2_pkt_decode_retry(&retry, pkt + hdpktlen, pktlen - hdpktlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ retry.odcid = conn->dcid.current.cid;
+
+ rv = ngtcp2_pkt_verify_retry_tag(
+ conn->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,
+ "unable to verify Retry packet integrity");
+ return rv;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s",
+ (const char *)ngtcp2_encode_hex(cidbuf, retry.odcid.data,
+ retry.odcid.datalen));
+
+ if (retry.token.len == 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid)) {
+ return 0;
+ }
+
+ ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd);
+
+ /* DCID must be updated before invoking callback because client
+ generates new initial keys there. */
+ conn->dcid.current.cid = hd->scid;
+ conn->retry_scid = hd->scid;
+
+ conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY;
+
+ assert(conn->callbacks.recv_retry);
+
+ rv = conn->callbacks.recv_retry(conn, hd, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ conn->state = NGTCP2_CS_CLIENT_INITIAL;
+
+ /* Just freeing memory is dangerous because we might free twice. */
+
+ rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat);
+ if (rv != 0) {
+ return rv;
+ }
+
+ token = &conn->local.settings.token;
+
+ ngtcp2_mem_free(conn->mem, token->base);
+ token->base = NULL;
+ token->len = 0;
+
+ token->base = ngtcp2_mem_malloc(conn->mem, retry.token.len);
+ if (token->base == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ token->len = retry.token.len;
+
+ ngtcp2_cpymem(token->base, retry.token.base, retry.token.len);
+
+ reset_conn_stat_recovery(&conn->cstat);
+ conn_reset_congestion_state(conn);
+ conn_reset_ecn_validation_state(conn);
+
+ return 0;
+}
+
+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;
+}
+
+/*
+ * conn_recv_ack processes received ACK frame |fr|. |pkt_ts| is the
+ * timestamp when packet is received. |ts| should be the current
+ * time. Usually they are the same, but for buffered packets,
+ * |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_PROTO
+ * |fr| acknowledges a packet this endpoint has not sent.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ */
+static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_frame_chain *frc = NULL;
+ ngtcp2_ssize num_acked;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+
+ if (pktns->tx.last_pkt_num < fr->largest_ack) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ rv = ngtcp2_pkt_validate_ack(fr);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_acktr_recv_ack(&pktns->acktr, fr);
+
+ num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns,
+ pkt_ts, ts);
+ if (num_acked < 0) {
+ /* TODO assert this */
+ assert(ngtcp2_err_is_fatal((int)num_acked));
+ ngtcp2_frame_chain_list_del(frc, conn->mem);
+ return (int)num_acked;
+ }
+
+ if (num_acked == 0) {
+ 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 &&
+ (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) {
+ /* Reset PTO count but no less than 2 to avoid frequent probe
+ packet transmission. */
+ cstat->pto_count = ngtcp2_min(cstat->pto_count, 2);
+ }
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ return 0;
+}
+
+/*
+ * conn_assign_recved_ack_delay_unscaled assigns
+ * fr->ack_delay_unscaled.
+ */
+static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr,
+ uint64_t ack_delay_exponent) {
+ fr->ack_delay_unscaled =
+ fr->ack_delay * (1ULL << ack_delay_exponent) * NGTCP2_MICROSECONDS;
+}
+
+/*
+ * conn_recv_max_stream_data processes received MAX_STREAM_DATA frame
+ * |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * Stream ID indicates that it is a local stream, and the local
+ * endpoint has not initiated it; or stream is peer initiated
+ * unidirectional stream.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * Stream ID exceeds allowed limit.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_recv_max_stream_data(ngtcp2_conn *conn,
+ const ngtcp2_max_stream_data *fr) {
+ ngtcp2_strm *strm;
+ ngtcp2_idtr *idtr;
+ int local_stream = conn_local_stream(conn, fr->stream_id);
+ int bidi = bidi_stream(fr->stream_id);
+ int rv;
+
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ /* Stream has been closed. */
+ return 0;
+ }
+
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ /* Stream has been closed. */
+ return 0;
+ }
+
+ strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ 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);
+ return rv;
+ }
+ }
+
+ if (strm->tx.max_offset < fr->max_stream_data) {
+ strm->tx.max_offset = fr->max_stream_data;
+
+ /* 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, fr->stream_id,
+ fr->max_stream_data);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_max_data processes received MAX_DATA frame |fr|.
+ */
+static void conn_recv_max_data(ngtcp2_conn *conn, const ngtcp2_max_data *fr) {
+ conn->tx.max_offset = ngtcp2_max(conn->tx.max_offset, fr->max_data);
+}
+
+/*
+ * conn_buffer_pkt buffers |pkt| of length |pktlen|, chaining it from
+ * |*ppc|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_buffer_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ const ngtcp2_path *path, const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen, size_t dgramlen,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_pkt_chain **ppc = &pktns->rx.buffed_pkts, *pc;
+ size_t i;
+ for (i = 0; *ppc && i < NGTCP2_MAX_NUM_BUFFED_RX_PKTS;
+ ppc = &(*ppc)->next, ++i)
+ ;
+
+ if (i == NGTCP2_MAX_NUM_BUFFED_RX_PKTS) {
+ return 0;
+ }
+
+ rv =
+ ngtcp2_pkt_chain_new(&pc, path, pi, pkt, pktlen, dgramlen, ts, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *ppc = pc;
+
+ return 0;
+}
+
+static int ensure_decrypt_buffer(ngtcp2_vec *vec, size_t n, size_t initial,
+ const ngtcp2_mem *mem) {
+ uint8_t *nbuf;
+ size_t len;
+
+ if (vec->len >= n) {
+ return 0;
+ }
+
+ len = vec->len == 0 ? initial : vec->len * 2;
+ for (; len < n; len *= 2)
+ ;
+ nbuf = ngtcp2_mem_realloc(mem, vec->base, len);
+ if (nbuf == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ vec->base = nbuf;
+ vec->len = len;
+
+ return 0;
+}
+
+/*
+ * conn_ensure_decrypt_hp_buffer ensures that
+ * conn->crypto.decrypt_hp_buf has at least |n| bytes space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_ensure_decrypt_hp_buffer(ngtcp2_conn *conn, size_t n) {
+ return ensure_decrypt_buffer(&conn->crypto.decrypt_hp_buf, n, 256, conn->mem);
+}
+
+/*
+ * conn_ensure_decrypt_buffer ensures that conn->crypto.decrypt_buf
+ * has at least |n| bytes space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_ensure_decrypt_buffer(ngtcp2_conn *conn, size_t n) {
+ return ensure_decrypt_buffer(&conn->crypto.decrypt_buf, n, 2048, conn->mem);
+}
+
+/*
+ * 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.
+ *
+ * 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.
+ */
+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,
+ int64_t pkt_num, ngtcp2_crypto_km *ckm,
+ ngtcp2_decrypt decrypt) {
+ /* TODO nonce is limited to 64 bytes. */
+ uint8_t nonce[64];
+ int rv;
+
+ assert(sizeof(nonce) >= ckm->iv.len);
+
+ 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);
+
+ if (rv != 0) {
+ if (rv == NGTCP2_ERR_TLS_DECRYPT) {
+ return rv;
+ }
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ assert(payloadlen >= aead->max_overhead);
+
+ return (ngtcp2_ssize)(payloadlen - aead->max_overhead);
+}
+
+/*
+ * decrypt_hp decryptes packet header. The packet number starts at
+ * |pkt| + |pkt_num_offset|. The entire plaintext QUIC packet header
+ * will be written to the buffer pointed by |dest| whose capacity is
+ * |destlen|.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Packet is badly formatted
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * 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) {
+ size_t sample_offset;
+ uint8_t *p = dest;
+ uint8_t mask[NGTCP2_HP_MASKLEN];
+ size_t i;
+ int rv;
+
+ assert(hp_mask);
+ assert(ckm);
+
+ if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ p = ngtcp2_cpymem(p, pkt, pkt_num_offset);
+
+ sample_offset = pkt_num_offset + 4;
+
+ rv = hp_mask(mask, hp, hp_ctx, pkt + sample_offset);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0f));
+ } else {
+ dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1f));
+ if (dest[0] & NGTCP2_SHORT_KEY_PHASE_BIT) {
+ hd->flags |= NGTCP2_PKT_FLAG_KEY_PHASE;
+ }
+ }
+
+ hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1);
+
+ for (i = 0; i < hd->pkt_numlen; ++i) {
+ *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1];
+ }
+
+ hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen);
+
+ return p - dest;
+}
+
+/*
+ * conn_emit_pending_crypto_data delivers pending stream data to the
+ * application due to packet reordering.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed
+ * NGTCP2_ERR_CRYPTO
+ * TLS backend reported error
+ */
+static int conn_emit_pending_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ ngtcp2_strm *strm,
+ uint64_t rx_offset) {
+ size_t datalen;
+ const uint8_t *data;
+ int rv;
+ uint64_t offset;
+
+ if (!strm->rx.rob) {
+ return 0;
+ }
+
+ for (;;) {
+ datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset);
+ if (datalen == 0) {
+ assert(rx_offset == ngtcp2_strm_rx_offset(strm));
+ return 0;
+ }
+
+ offset = rx_offset;
+ rx_offset += datalen;
+
+ rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen);
+ }
+}
+
+/*
+ * 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) {
+ conn->state = NGTCP2_CS_DRAINING;
+ if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT;
+ } else {
+ conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION;
+ }
+ conn->rx.ccec.error_code = fr->error_code;
+}
+
+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");
+ return;
+ }
+
+ ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge);
+ 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) {
+ conn_reset_conn_stat_cc(conn, &conn->cstat);
+
+ conn->cc.reset(&conn->cc);
+
+ if (conn->hs_pktns) {
+ ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb,
+ conn->hs_pktns->tx.last_pkt_num + 1);
+ }
+ ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1);
+ ngtcp2_rst_init(&conn->rst);
+}
+
+static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_duration pto, timeout;
+ ngtcp2_pv *pv = conn->pv, *npv;
+ uint8_t ent_flags;
+
+ if (!pv) {
+ return 0;
+ }
+
+ 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);
+ }
+ return 0;
+ }
+
+ if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) {
+ if (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) {
+ if (pv->dcid.seq != conn->dcid.current.seq) {
+ assert(conn->dcid.current.cid.datalen);
+
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
+ if (rv != 0) {
+ goto fail;
+ }
+ ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
+ }
+ conn_reset_congestion_state(conn);
+ conn_reset_ecn_validation_state(conn);
+ }
+
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) {
+ conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ }
+
+ rv = conn_call_path_validation(conn, &pv->dcid.ps.path,
+ NGTCP2_PATH_VALIDATION_RESULT_SUCCESS);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
+ pto = conn_compute_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, pv->fallback_pto);
+
+ if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) {
+ assert(conn->server);
+
+ /* Validate path again */
+ rv = ngtcp2_pv_new(&npv, &pv->dcid, timeout,
+ NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE |
+ NGTCP2_PV_FLAG_MTU_PROBE,
+ &conn->log, conn->mem);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ ngtcp2_dcid_copy(&npv->fallback_dcid, &pv->fallback_dcid);
+ npv->fallback_pto = pv->fallback_pto;
+ } else {
+ rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, timeout,
+ NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ /* Unset the flag bit so that conn_stop_pv does not retire
+ DCID. */
+ pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE;
+
+ rv = conn_stop_pv(conn, ts);
+ if (rv != 0) {
+ ngtcp2_pv_del(npv);
+ return rv;
+ }
+
+ conn->pv = npv;
+
+ return 0;
+ }
+
+fail:
+ return conn_stop_pv(conn, ts);
+}
+
+/*
+ * pkt_num_bits returns the number of bits available when packet
+ * number is encoded in |pkt_numlen| bytes.
+ */
+static size_t pkt_num_bits(size_t pkt_numlen) {
+ switch (pkt_numlen) {
+ case 1:
+ return 8;
+ case 2:
+ return 16;
+ case 3:
+ return 24;
+ case 4:
+ return 32;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+/*
+ * pktns_pkt_num_is_duplicate returns nonzero if |pkt_num| is
+ * duplicated packet number.
+ */
+static int pktns_pkt_num_is_duplicate(ngtcp2_pktns *pktns, int64_t pkt_num) {
+ return ngtcp2_gaptr_is_pushed(&pktns->rx.pngap, (uint64_t)pkt_num, 1);
+}
+
+/*
+ * pktns_commit_recv_pkt_num marks packet number |pkt_num| as
+ * received.
+ */
+static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num,
+ int ack_eliciting, ngtcp2_tstamp ts) {
+ int rv;
+
+ if (ack_eliciting && pktns->rx.max_ack_eliciting_pkt_num + 1 != pkt_num) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+ if (pktns->rx.max_pkt_num < pkt_num) {
+ pktns->rx.max_pkt_num = pkt_num;
+ pktns->rx.max_pkt_ts = ts;
+ }
+ if (ack_eliciting && pktns->rx.max_ack_eliciting_pkt_num < pkt_num) {
+ pktns->rx.max_ack_eliciting_pkt_num = pkt_num;
+ }
+
+ rv = ngtcp2_gaptr_push(&pktns->rx.pngap, (uint64_t)pkt_num, 1);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_ksl_len(&pktns->rx.pngap.gap) > 256) {
+ ngtcp2_gaptr_drop_first_gap(&pktns->rx.pngap);
+ }
+
+ return 0;
+}
+
+/*
+ * verify_token verifies |hd| contains |token| in its token field. It
+ * returns 0 if it succeeds, or NGTCP2_ERR_PROTO.
+ */
+static int verify_token(const ngtcp2_vec *token, const ngtcp2_pkt_hd *hd) {
+ if (token->len == hd->token.len &&
+ ngtcp2_cmemeq(token->base, hd->token.base, token->len)) {
+ return 0;
+ }
+ return NGTCP2_ERR_PROTO;
+}
+
+static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns,
+ const ngtcp2_pkt_info *pi) {
+ switch (pi->ecn & NGTCP2_ECN_MASK) {
+ case NGTCP2_ECN_ECT_0:
+ ++pktns->rx.ecn.ect0;
+ break;
+ case NGTCP2_ECN_ECT_1:
+ ++pktns->rx.ecn.ect1;
+ break;
+ case NGTCP2_ECN_CE:
+ ++pktns->rx.ecn.ce;
+ break;
+ }
+}
+
+static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
+ ngtcp2_strm *strm, const ngtcp2_crypto *fr);
+
+static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts);
+
+static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_tstamp ts);
+
+/*
+ * conn_recv_handshake_pkt processes received packet |pkt| whose
+ * length is |pktlen| during handshake period. The buffer pointed by
+ * |pkt| might contain multiple packets. This function only processes
+ * one packet. |pkt_ts| is the timestamp when packet is received.
+ * |ts| should be the current time. Usually they are the same, but
+ * for buffered packets, |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns the number of bytes it reads if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_RECV_VERSION_NEGOTIATION
+ * Version Negotiation packet is received.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_DISCARD_PKT
+ * Packet was discarded because plain text header was malformed;
+ * or its payload could not be decrypted.
+ * NGTCP2_ERR_FRAME_FORMAT
+ * Frame is badly formatted
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_CRYPTO
+ * TLS stack reported error.
+ * NGTCP2_ERR_PROTO
+ * Generic QUIC protocol error.
+ *
+ * In addition to the above error codes, error codes returned from
+ * conn_recv_pkt are also returned.
+ */
+static ngtcp2_ssize
+conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen, ngtcp2_tstamp pkt_ts,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_max_frame mfr;
+ ngtcp2_frame *fr = &mfr.fr;
+ int rv;
+ int require_ack = 0;
+ size_t hdpktlen;
+ const uint8_t *payload;
+ size_t payloadlen;
+ ngtcp2_ssize nwrite;
+ ngtcp2_crypto_aead *aead;
+ ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx *hp_ctx;
+ ngtcp2_hp_mask hp_mask;
+ ngtcp2_decrypt decrypt;
+ ngtcp2_pktns *pktns;
+ ngtcp2_strm *crypto;
+ ngtcp2_crypto_level crypto_level;
+ int invalid_reserved_bits = 0;
+
+ if (pktlen == 0) {
+ return 0;
+ }
+
+ 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. */
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering Short packet len=%zu", pktlen);
+
+ rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen,
+ ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen);
+ if (nread < 0) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ switch (hd.type) {
+ case NGTCP2_PKT_VERSION_NEGOTIATION:
+ hdpktlen = (size_t)nread;
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ if (conn->server) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Receiving Version Negotiation packet after getting Handshake
+ packet from server is invalid. */
+ if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!ngtcp2_cid_eq(&conn->oscid, &hd.dcid)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) {
+ /* Just discard invalid Version Negotiation packet */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched SCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ rv = conn_on_version_negotiation(conn, &hd, pkt + hdpktlen,
+ pktlen - hdpktlen);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ return NGTCP2_ERR_RECV_VERSION_NEGOTIATION;
+ case NGTCP2_PKT_RETRY:
+ hdpktlen = (size_t)nread;
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ if (conn->server) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Receiving Retry packet after getting Initial packet from server
+ is invalid. */
+ if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ if (pktlen < (size_t)nread + hd.len) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ pktlen = (size_t)nread + hd.len;
+
+ if (conn->version != hd.version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Quoted from spec: if subsequent packets of those types include a
+ different Source Connection ID, they MUST be discarded. */
+ if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) &&
+ !ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) {
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched SCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ switch (hd.type) {
+ case NGTCP2_PKT_0RTT:
+ if (!conn->server) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
+ if (conn->early.ckm) {
+ ngtcp2_ssize nread2;
+ /* TODO Avoid to parse header twice. */
+ nread2 =
+ conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, pkt_ts, ts);
+ if (nread2 < 0) {
+ return nread2;
+ }
+ }
+
+ /* Discard 0-RTT packet if we don't have a key to decrypt it. */
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ /* Buffer re-ordered 0-RTT packet. */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 0-RTT packet len=%zu", pktlen);
+
+ rv = conn_buffer_pkt(conn, conn->in_pktns, path, pi, pkt, pktlen, dgramlen,
+ ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ return (ngtcp2_ssize)pktlen;
+ case NGTCP2_PKT_INITIAL:
+ if (!conn->in_pktns) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PKT,
+ "Initial packet is discarded because keys have been discarded");
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ assert(conn->in_pktns);
+
+ if (conn->server) {
+ if (conn->local.settings.token.len) {
+ rv = verify_token(&conn->local.settings.token, &hd);
+ if (rv != 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because token is invalid");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ }
+ if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) == 0) {
+ /* Set rcid here so that it is available to callback. If this
+ packet is discarded later in this function and no packet is
+ processed in this connection attempt so far, connection
+ will be dropped. */
+ conn->rcid = hd.dcid;
+
+ rv = conn_call_recv_client_initial(conn, &hd.dcid);
+ if (rv != 0) {
+ 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;
+ }
+
+ pktns = conn->in_pktns;
+ crypto = &pktns->crypto.strm;
+ crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL;
+
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ if (!conn->hs_pktns->crypto.rx.ckm) {
+ if (conn->server) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PKT,
+ "Handshake packet at this point is unexpected and discarded");
+ return (ngtcp2_ssize)pktlen;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering Handshake packet len=%zu", pktlen);
+
+ rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pi, pkt, pktlen,
+ dgramlen, ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ pktns = conn->hs_pktns;
+ crypto = &pktns->crypto.strm;
+ crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+
+ break;
+ default:
+ /* unknown packet type */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of unknown packet type");
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ hp_mask = conn->callbacks.hp_mask;
+ 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);
+ assert(decrypt);
+
+ rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
+ (size_t)nread, ckm, hp_ctx, hp_mask);
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ hdpktlen = (size_t)nwrite;
+ payload = pkt + hdpktlen;
+ payloadlen = hd.len - hd.pkt_numlen;
+
+ hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num,
+ pkt_num_bits(hd.pkt_numlen));
+ if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ rv = ngtcp2_pkt_verify_reserved_bits(conn->crypto.decrypt_hp_buf.base[0]);
+ if (rv != 0) {
+ invalid_reserved_bits = 1;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet has incorrect reserved bits");
+
+ /* Will return error after decrypting payload */
+ }
+
+ if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was discarded because of duplicated packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = conn_ensure_decrypt_buffer(conn, payloadlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen,
+ conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num,
+ ckm, decrypt);
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet payload");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (invalid_reserved_bits) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ payload = conn->crypto.decrypt_buf.base;
+ payloadlen = (size_t)nwrite;
+
+ switch (hd.type) {
+ case NGTCP2_PKT_INITIAL:
+ if (!conn->server || ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) &&
+ !ngtcp2_cid_eq(&conn->rcid, &hd.dcid))) {
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ }
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ if (payloadlen == 0) {
+ /* QUIC packet must contain at least one frame */
+ if (hd.type == NGTCP2_PKT_INITIAL) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (hd.type == NGTCP2_PKT_INITIAL &&
+ !(conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED)) {
+ conn->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED;
+ if (!conn->server) {
+ conn->dcid.current.cid = hd.scid;
+ }
+ }
+
+ ngtcp2_qlog_pkt_received_start(&conn->qlog);
+
+ for (; payloadlen;) {
+ nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ if (nread < 0) {
+ return nread;
+ }
+
+ payload += nread;
+ payloadlen -= (size_t)nread;
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ fr->ack.ack_delay = 0;
+ fr->ack.ack_delay_unscaled = 0;
+ break;
+ }
+
+ ngtcp2_log_rx_fr(&conn->log, &hd, fr);
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) {
+ conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+ }
+ rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_PADDING:
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto);
+ if (rv != 0) {
+ return rv;
+ }
+ require_ack = 1;
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ conn_recv_connection_close(conn, &fr->connection_close);
+ break;
+ case NGTCP2_FRAME_PING:
+ require_ack = 1;
+ break;
+ default:
+ return NGTCP2_ERR_PROTO;
+ }
+
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ }
+
+ if (conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) {
+ /* Successful processing of Handshake packet from client verifies
+ source address. */
+ conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ }
+
+ ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen);
+
+ rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns_increase_ecn_counts(pktns, pi);
+
+ /* TODO Initial and Handshake are always acknowledged without
+ delay. */
+ if (require_ack &&
+ (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh ||
+ (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+
+ rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack,
+ pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_restart_timer_on_read(conn, ts);
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING
+ : (ngtcp2_ssize)pktlen;
+}
+
+/*
+ * 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
+ * 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) {
+ ngtcp2_ssize nread;
+ size_t dgramlen = pktlen;
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn->dcid.current.bytes_recv += dgramlen;
+ }
+
+ while (pktlen) {
+ nread =
+ 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;
+ }
+
+ 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;
+ }
+
+ return NGTCP2_ERR_DROP_CONN;
+ }
+ 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;
+ }
+ return 0;
+ }
+
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ return 0;
+ }
+
+ return (int)nread;
+ }
+
+ assert(pktlen >= (size_t)nread);
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "read packet %td left %zu", nread, pktlen);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ int64_t stream_id, void *stream_user_data) {
+ int rv;
+ uint64_t max_rx_offset;
+ uint64_t max_tx_offset;
+ int local_stream = conn_local_stream(conn, stream_id);
+
+ 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;
+ } 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;
+ }
+ } else if (local_stream) {
+ max_rx_offset = 0;
+ 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;
+ }
+
+ rv = ngtcp2_map_insert(&conn->strms, &strm->me);
+ 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:
+ ngtcp2_strm_free(strm);
+ return rv;
+}
+
+/*
+ * conn_emit_pending_stream_data passes buffered ordered stream data
+ * to the application. |rx_offset| is the first offset to deliver to
+ * the application. This function assumes that the data up to
+ * |rx_offset| has been delivered already. This function only passes
+ * the ordered data without any gap. If there is a gap, it stops
+ * providing the data to the application, and returns.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t rx_offset) {
+ size_t datalen;
+ const uint8_t *data;
+ int rv;
+ uint64_t offset;
+ uint32_t sdflags;
+ int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED;
+
+ if (!strm->rx.rob) {
+ return 0;
+ }
+
+ for (;;) {
+ /* 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)) {
+ return 0;
+ }
+
+ datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset);
+ if (datalen == 0) {
+ assert(rx_offset == ngtcp2_strm_rx_offset(strm));
+ return 0;
+ }
+
+ offset = rx_offset;
+ rx_offset += datalen;
+
+ sdflags = NGTCP2_STREAM_DATA_FLAG_NONE;
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ rx_offset == strm->rx.last_offset) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
+ }
+ if (!handshake_completed) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY;
+ }
+
+ rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen);
+ }
+}
+
+/*
+ * conn_recv_crypto is called when CRYPTO frame |fr| is received.
+ * |rx_offset_base| is the offset in the entire TLS handshake stream.
+ * fr->offset specifies the offset in each encryption level.
+ * |max_rx_offset| is, if it is nonzero, the maximum offset in the
+ * entire TLS handshake stream that |fr| can carry. |crypto_level| is
+ * the encryption level where this data is received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * CRYPTO frame has invalid offset.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CRYPTO
+ * TLS stack reported error.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * The end offset exceeds the maximum value.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
+ ngtcp2_strm *crypto, const ngtcp2_crypto *fr) {
+ uint64_t fr_end_offset;
+ uint64_t rx_offset;
+ int rv;
+
+ if (fr->datacnt == 0) {
+ return 0;
+ }
+
+ fr_end_offset = fr->offset + fr->data[0].len;
+
+ if (NGTCP2_MAX_VARINT < fr_end_offset) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ rx_offset = ngtcp2_strm_rx_offset(crypto);
+
+ if (fr_end_offset <= rx_offset) {
+ if (conn->server &&
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT) &&
+ crypto_level == NGTCP2_CRYPTO_LEVEL_INITIAL) {
+ /* recovery draft: Speeding Up Handshake Completion
+
+ When a server receives an Initial packet containing duplicate
+ CRYPTO data, it can assume the client did not receive all of
+ the server's CRYPTO data sent in Initial packets, or the
+ client's estimated RTT is too small. ... To speed up
+ handshake completion under these conditions, an endpoint MAY
+ send a packet containing unacknowledged CRYPTO data earlier
+ than the PTO expiry, subject to address validation limits;
+ ... */
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT;
+ conn->in_pktns->rtb.probe_pkt_left = 1;
+ conn->hs_pktns->rtb.probe_pkt_left = 1;
+ }
+ return 0;
+ }
+
+ crypto->rx.last_offset = ngtcp2_max(crypto->rx.last_offset, fr_end_offset);
+
+ /* TODO Before dispatching incoming data to TLS stack, make sure
+ that previous data in previous encryption level has been
+ completely sent to TLS stack. Usually, if data is left, it is an
+ error because key is generated after consuming all data in the
+ previous encryption level. */
+ if (fr->offset <= rx_offset) {
+ size_t ncut = (size_t)(rx_offset - fr->offset);
+ const uint8_t *data = fr->data[0].base + ncut;
+ size_t datalen = fr->data[0].len - ncut;
+ uint64_t offset = rx_offset;
+
+ rx_offset += datalen;
+ rv = ngtcp2_strm_update_rx_offset(crypto, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_emit_pending_crypto_data(conn, crypto_level, crypto, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+ }
+
+ if (fr_end_offset - rx_offset > NGTCP2_MAX_REORDERED_CRYPTO_DATA) {
+ return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED;
+ }
+
+ return ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len,
+ fr->offset);
+}
+
+/*
+ * conn_max_data_violated returns nonzero if receiving |datalen|
+ * violates connection flow control on local endpoint.
+ */
+static int conn_max_data_violated(ngtcp2_conn *conn, uint64_t datalen) {
+ return conn->rx.max_offset - conn->rx.offset < datalen;
+}
+
+/*
+ * conn_recv_stream is called when STREAM frame |fr| is received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * STREAM frame is received for a local stream which is not
+ * initiated; or STREAM frame is received for a local
+ * unidirectional stream
+ * NGTCP2_ERR_STREAM_LIMIT
+ * STREAM frame has remote stream ID which is strictly greater
+ * than the allowed limit.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_FLOW_CONTROL
+ * Flow control limit is violated; or the end offset of stream
+ * data is beyond the NGTCP2_MAX_VARINT.
+ * NGTCP2_ERR_FINAL_SIZE
+ * STREAM frame has strictly larger end offset than it is
+ * permitted.
+ */
+static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
+ int rv;
+ ngtcp2_strm *strm;
+ ngtcp2_idtr *idtr;
+ uint64_t rx_offset, fr_end_offset;
+ int local_stream;
+ int bidi;
+ size_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);
+ bidi = bidi_stream(fr->stream_id);
+
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (local_stream) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ if (NGTCP2_MAX_VARINT - datalen < fr->offset) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ /* TODO The stream has been closed. This should be responded
+ with RESET_STREAM, or simply ignored. */
+ return 0;
+ }
+
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ /* TODO The stream has been closed. This should be responded
+ with RESET_STREAM, or simply ignored. */
+ return 0;
+ }
+
+ strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ 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);
+ return rv;
+ }
+ if (!bidi) {
+ ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR);
+ }
+ }
+
+ fr_end_offset = fr->offset + datalen;
+
+ if (strm->rx.max_offset < fr_end_offset) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ if (strm->rx.last_offset < fr_end_offset) {
+ uint64_t len = fr_end_offset - strm->rx.last_offset;
+
+ if (conn_max_data_violated(conn, len)) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ conn->rx.offset += len;
+
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
+ ngtcp2_conn_extend_max_offset(conn, len);
+ }
+ }
+
+ rx_offset = ngtcp2_strm_rx_offset(strm);
+
+ if (fr->fin) {
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) {
+ if (strm->rx.last_offset != fr_end_offset) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+
+ if (strm->flags &
+ (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) {
+ return 0;
+ }
+
+ if (rx_offset == fr_end_offset) {
+ return 0;
+ }
+ } else if (strm->rx.last_offset > fr_end_offset) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ } else {
+ 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) &&
+ strm->rx.last_offset < fr_end_offset) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+
+ strm->rx.last_offset = ngtcp2_max(strm->rx.last_offset, fr_end_offset);
+
+ if (fr_end_offset <= rx_offset) {
+ return 0;
+ }
+
+ if (strm->flags &
+ (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) {
+ return 0;
+ }
+ }
+
+ if (fr->offset <= rx_offset) {
+ size_t ncut = (size_t)(rx_offset - fr->offset);
+ uint64_t offset = rx_offset;
+ const uint8_t *data;
+ int fin;
+
+ if (fr->datacnt) {
+ data = fr->data[0].base + ncut;
+ datalen -= ncut;
+
+ rx_offset += datalen;
+ rv = ngtcp2_strm_update_rx_offset(strm, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ data = NULL;
+ datalen = 0;
+ }
+
+ fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ rx_offset == strm->rx.last_offset;
+
+ if (fin || datalen) {
+ if (fin) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
+ }
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY;
+ }
+ rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
+ datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_emit_pending_stream_data(conn, strm, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else if (fr->datacnt) {
+ rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len,
+ fr->offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR);
+}
+
+/*
+ * conn_reset_stream adds RESET_STREAM frame to the transmission
+ * queue.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ int rv;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frc->fr.type = NGTCP2_FRAME_RESET_STREAM;
+ frc->fr.reset_stream.stream_id = strm->stream_id;
+ frc->fr.reset_stream.app_error_code = app_error_code;
+ frc->fr.reset_stream.final_size = strm->tx.offset;
+
+ /* TODO This prepends RESET_STREAM to pktns->tx.frq. */
+ frc->next = pktns->tx.frq;
+ pktns->tx.frq = frc;
+
+ return 0;
+}
+
+/*
+ * conn_stop_sending adds STOP_SENDING frame to the transmission
+ * queue.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ int rv;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frc->fr.type = NGTCP2_FRAME_STOP_SENDING;
+ frc->fr.stop_sending.stream_id = strm->stream_id;
+ frc->fr.stop_sending.app_error_code = app_error_code;
+
+ /* TODO This prepends STOP_SENDING to pktns->tx.frq. */
+ frc->next = pktns->tx.frq;
+ pktns->tx.frq = frc;
+
+ return 0;
+}
+
+/*
+ * handle_max_remote_streams_extension extends
+ * |*punsent_max_remote_streams| by |n| if a condition allows it.
+ */
+static void
+handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams,
+ size_t n) {
+ if (
+#if SIZE_MAX > UINT32_MAX
+ NGTCP2_MAX_STREAMS < n ||
+#endif /* SIZE_MAX > UINT32_MAX */
+ *punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) {
+ *punsent_max_remote_streams = NGTCP2_MAX_STREAMS;
+ } else {
+ *punsent_max_remote_streams += n;
+ }
+}
+
+/*
+ * conn_recv_reset_stream is called when RESET_STREAM |fr| is
+ * received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * RESET_STREAM frame is received to the local stream which is not
+ * initiated.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * RESET_STREAM frame has remote stream ID which is strictly
+ * greater than the allowed limit.
+ * NGTCP2_ERR_PROTO
+ * RESET_STREAM frame is received to the local unidirectional
+ * stream
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_FLOW_CONTROL
+ * Flow control limit is violated; or the final size is beyond the
+ * NGTCP2_MAX_VARINT.
+ * NGTCP2_ERR_FINAL_SIZE
+ * The final offset is strictly larger than it is permitted.
+ */
+static int conn_recv_reset_stream(ngtcp2_conn *conn,
+ const ngtcp2_reset_stream *fr) {
+ ngtcp2_strm *strm;
+ int local_stream = conn_local_stream(conn, fr->stream_id);
+ int bidi = bidi_stream(fr->stream_id);
+ uint64_t datalen;
+ ngtcp2_idtr *idtr;
+ int rv;
+
+ /* TODO share this piece of code */
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (local_stream) {
+ return NGTCP2_ERR_PROTO;
+ }
+ if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ if (NGTCP2_MAX_VARINT < fr->final_size) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ return 0;
+ }
+
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ return 0;
+ }
+
+ if (conn_initial_stream_rx_offset(conn, fr->stream_id) < fr->final_size ||
+ conn_max_data_violated(conn, fr->final_size)) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ /* Stream is reset before we create ngtcp2_strm object. */
+ conn->rx.offset += fr->final_size;
+ ngtcp2_conn_extend_max_offset(conn, fr->final_size);
+
+ rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size,
+ fr->app_error_code, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* There will be no activity in this stream because we got
+ RESET_STREAM and don't write stream data any further. This
+ effectively allows another new stream for peer. */
+ if (bidi) {
+ handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams,
+ 1);
+ } else {
+ handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams,
+ 1);
+ }
+
+ return 0;
+ }
+
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) {
+ if (strm->rx.last_offset != fr->final_size) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+ } else if (strm->rx.last_offset > fr->final_size) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+
+ datalen = fr->final_size - strm->rx.last_offset;
+
+ if (strm->rx.max_offset < fr->final_size ||
+ 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;
+ }
+
+ /* 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;
+ ngtcp2_conn_extend_max_offset(conn, datalen);
+
+ 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);
+}
+
+/*
+ * conn_recv_stop_sending is called when STOP_SENDING |fr| is received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * STOP_SENDING frame is received for a local stream which is not
+ * initiated; or STOP_SENDING frame is received for a local
+ * unidirectional stream.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * STOP_SENDING frame has remote stream ID which is strictly
+ * greater than the allowed limit.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_recv_stop_sending(ngtcp2_conn *conn,
+ const ngtcp2_stop_sending *fr) {
+ int rv;
+ ngtcp2_strm *strm;
+ ngtcp2_idtr *idtr;
+ int local_stream = conn_local_stream(conn, fr->stream_id);
+ int bidi = bidi_stream(fr->stream_id);
+
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ return 0;
+ }
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ return 0;
+ }
+
+ /* Frame is received reset before we create ngtcp2_strm
+ object. */
+ strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ 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);
+ return rv;
+ }
+ }
+
+ /* 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;
+ }
+
+ strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST;
+
+ ngtcp2_strm_streamfrq_clear(strm);
+
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code);
+}
+
+/*
+ * check_stateless_reset returns nonzero if Stateless Reset |sr|
+ * coming via |path| is valid against |dcid|.
+ */
+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;
+}
+
+/*
+ * conn_on_stateless_reset decodes Stateless Reset from the buffer
+ * pointed by |payload| whose length is |payloadlen|. |payload|
+ * should start after first byte of packet.
+ *
+ * If Stateless Reset is decoded, and the Stateless Reset Token is
+ * validated, the connection is closed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Could not decode Stateless Reset; or Stateless Reset Token does
+ * not match; or No stateless reset token is available.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ */
+static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const uint8_t *payload, size_t payloadlen) {
+ int rv = 1;
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_dcid *dcid;
+ ngtcp2_pkt_stateless_reset sr;
+ size_t len, i;
+
+ rv = ngtcp2_pkt_decode_stateless_reset(&sr, payload, payloadlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!check_stateless_reset(&conn->dcid.current, path, &sr) &&
+ (!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);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+ if (check_stateless_reset(dcid, path, &sr)) {
+ break;
+ }
+ }
+
+ if (i == len) {
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+ if (check_stateless_reset(dcid, path, &sr)) {
+ break;
+ }
+ }
+
+ if (i == len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ }
+ }
+
+ conn->state = NGTCP2_CS_DRAINING;
+
+ 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;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_max_streams processes the incoming MAX_STREAMS frame
+ * |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * The maximum streams field exceeds the maximum value.
+ */
+static int conn_recv_max_streams(ngtcp2_conn *conn,
+ const ngtcp2_max_streams *fr) {
+ uint64_t n;
+
+ if (fr->max_streams > NGTCP2_MAX_STREAMS) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ n = ngtcp2_min(fr->max_streams, NGTCP2_MAX_STREAMS);
+
+ if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) {
+ if (conn->local.bidi.max_streams < n) {
+ conn->local.bidi.max_streams = n;
+ return conn_call_extend_max_local_streams_bidi(conn, n);
+ }
+ return 0;
+ }
+
+ if (conn->local.uni.max_streams < n) {
+ conn->local.uni.max_streams = n;
+ return conn_call_extend_max_local_streams_uni(conn, n);
+ }
+ return 0;
+}
+
+static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb,
+ uint64_t retire_prior_to) {
+ size_t i;
+ ngtcp2_dcid *dcid, *last;
+ int rv;
+
+ for (i = 0; i < ngtcp2_ringbuf_len(rb);) {
+ dcid = ngtcp2_ringbuf_get(rb, i);
+ if (dcid->seq >= retire_prior_to) {
+ ++i;
+ continue;
+ }
+
+ rv = conn_retire_dcid_seq(conn, dcid->seq);
+ if (rv != 0) {
+ return rv;
+ }
+ if (i == 0) {
+ ngtcp2_ringbuf_pop_front(rb);
+ } else 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);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_new_connection_id processes the incoming
+ * NEW_CONNECTION_ID frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * |fr| has the duplicated sequence number with different CID or
+ * token; or DCID is zero-length.
+ */
+static int conn_recv_new_connection_id(ngtcp2_conn *conn,
+ const ngtcp2_new_connection_id *fr) {
+ size_t i, len;
+ ngtcp2_dcid *dcid;
+ ngtcp2_pv *pv = conn->pv;
+ int rv;
+ int found = 0;
+ size_t extra_dcid = 0;
+
+ if (conn->dcid.current.cid.datalen == 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (fr->retire_prior_to > fr->seq) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ rv = ngtcp2_dcid_verify_uniqueness(&conn->dcid.current, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ngtcp2_cid_eq(&conn->dcid.current.cid, &fr->cid)) {
+ found = 1;
+ }
+
+ if (pv) {
+ rv = ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ngtcp2_cid_eq(&pv->dcid.cid, &fr->cid)) {
+ found = 1;
+ }
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+ rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+ if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) {
+ found = 1;
+ }
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.unused);
+
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i);
+ rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+ if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) {
+ found = 1;
+ }
+ }
+
+ 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);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused,
+ conn->dcid.retire_prior_to);
+ if (rv != 0) {
+ return rv;
+ }
+ } else if (fr->seq < conn->dcid.retire_prior_to) {
+ /* If packets are reordered, we might have retire_prior_to which
+ is larger than fr->seq.
+
+ A malicious peer might send crafted NEW_CONNECTION_ID to force
+ local endpoint to create lots of RETIRE_CONNECTION_ID frames.
+ 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;
+ }
+
+ if (found) {
+ return 0;
+ }
+
+ if (ngtcp2_gaptr_is_pushed(&conn->dcid.seqgap, fr->seq, 1)) {
+ return 0;
+ }
+
+ rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, fr->seq, 1);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_ksl_len(&conn->dcid.seqgap.gap) > 32) {
+ ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap);
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.unused);
+
+ if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) {
+ ++extra_dcid;
+ }
+ if (pv) {
+ if (pv->dcid.seq != conn->dcid.current.seq &&
+ pv->dcid.seq >= conn->dcid.retire_prior_to) {
+ ++extra_dcid;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq >= conn->dcid.retire_prior_to) {
+ ++extra_dcid;
+ }
+ }
+
+ if (conn->local.transport_params.active_connection_id_limit <=
+ len + extra_dcid) {
+ return NGTCP2_ERR_CONNECTION_ID_LIMIT;
+ }
+
+ if (len >= NGTCP2_MAX_DCID_POOL_SIZE) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "too many connection ID");
+ return 0;
+ }
+
+ dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused);
+ ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token);
+
+ return 0;
+}
+
+/*
+ * conn_post_process_recv_new_connection_id handles retirement request
+ * of active DCIDs.
+ *
+ * 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_post_process_recv_new_connection_id(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_dcid *dcid;
+ int rv;
+
+ if (conn->dcid.current.seq < conn->dcid.retire_prior_to) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+ return 0;
+ }
+
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ if (pv) {
+ if (conn->dcid.current.seq == pv->dcid.seq) {
+ ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ conn->dcid.current.seq == pv->fallback_dcid.seq) {
+ ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
+ }
+ }
+
+ ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (pv) {
+ if (pv->dcid.seq < conn->dcid.retire_prior_to) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused)) {
+ rv = conn_retire_dcid(conn, &pv->dcid, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->dcid.seq == pv->fallback_dcid.seq) {
+ ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
+ }
+
+ ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+
+ rv = conn_call_activate_dcid(conn, &pv->dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
+ "path migration is aborted because connection ID is"
+ "retired and no unused connection ID is available");
+
+ return conn_stop_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)) {
+ rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+
+ rv = conn_call_activate_dcid(conn, &pv->fallback_dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ /* Now we have no fallback dcid. */
+ return conn_stop_pv(conn, ts);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_retire_connection_id processes the incoming
+ * RETIRE_CONNECTION_ID frame |fr|. |hd| is a packet header which
+ * |fr| is included.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_PROTO
+ * SCID is zero-length.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Attempt to retire CID which is used as DCID to send this frame.
+ */
+static int conn_recv_retire_connection_id(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const ngtcp2_retire_connection_id *fr,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+
+ if (conn->oscid.datalen == 0 || conn->scid.last_seq < fr->seq) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ scid = ngtcp2_ksl_it_get(&it);
+ if (scid->seq == fr->seq) {
+ if (ngtcp2_cid_eq(&scid->cid, &hd->dcid)) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (!(scid->flags & NGTCP2_SCID_FLAG_RETIRED)) {
+ scid->flags |= NGTCP2_SCID_FLAG_RETIRED;
+ ++conn->scid.num_retired;
+ }
+
+ if (scid->pe.index != NGTCP2_PQ_BAD_INDEX) {
+ ngtcp2_pq_remove(&conn->scid.used, &scid->pe);
+ scid->pe.index = NGTCP2_PQ_BAD_INDEX;
+ }
+
+ scid->ts_retired = ts;
+
+ return ngtcp2_pq_push(&conn->scid.used, &scid->pe);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_new_token processes the incoming NEW_TOKEN frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Token is empty
+ * NGTCP2_ERR_PROTO:
+ * 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;
+ }
+
+ if (fr->token.len == 0) {
+ 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;
+}
+
+/*
+ * conn_recv_streams_blocked_bidi processes the incoming
+ * STREAMS_BLOCKED (0x16).
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Maximum Streams is larger than advertised value.
+ */
+static int conn_recv_streams_blocked_bidi(ngtcp2_conn *conn,
+ ngtcp2_streams_blocked *fr) {
+ if (fr->max_streams > conn->remote.bidi.max_streams) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_streams_blocked_uni processes the incoming
+ * STREAMS_BLOCKED (0x17).
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Maximum Streams is larger than advertised value.
+ */
+static int conn_recv_streams_blocked_uni(ngtcp2_conn *conn,
+ ngtcp2_streams_blocked *fr) {
+ if (fr->max_streams > conn->remote.uni.max_streams) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_select_preferred_addr asks a client application to select a
+ * server address from preferred addresses received from server. If a
+ * client chooses the address, path validation will start.
+ *
+ * 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_select_preferred_addr(ngtcp2_conn *conn) {
+ struct sockaddr_storage buf;
+ ngtcp2_addr addr;
+ 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) {
+ return 0;
+ }
+
+ rv = conn_call_select_preferred_addr(conn, &addr);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (addr.addrlen == 0 ||
+ ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) {
+ return 0;
+ }
+
+ assert(conn->pv == NULL);
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+ 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) {
+ /* TODO Call ngtcp2_dcid_free here if it is introduced */
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+ 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);
+}
+
+/*
+ * conn_recv_handshake_done processes the incoming HANDSHAKE_DONE
+ * frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Server received HANDSHAKE_DONE frame.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv;
+
+ if (conn->server) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) {
+ return 0;
+ }
+
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED |
+ NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+
+ conn->pktns.rtb.persistent_congestion_start_ts = ts;
+
+ conn_discard_handshake_state(conn, ts);
+
+ 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;
+ }
+ }
+
+ /* Re-arm loss detection timer after handshake has been
+ confirmed. */
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ return 0;
+}
+
+/*
+ * conn_recv_datagram processes the incoming DATAGRAM frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * 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;
+}
+
+/*
+ * conn_key_phase_changed returns nonzero if |hd| indicates that the
+ * key phase has unexpected value.
+ */
+static int conn_key_phase_changed(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ return !(pktns->crypto.rx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) ^
+ !(hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE);
+}
+
+/*
+ * conn_prepare_key_update installs new updated keys.
+ */
+static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_crypto_km *rx_ckm = pktns->crypto.rx.ckm;
+ ngtcp2_crypto_km *tx_ckm = pktns->crypto.tx.ckm;
+ ngtcp2_crypto_km *new_rx_ckm, *new_tx_ckm;
+ ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}, tx_aead_ctx = {0};
+ size_t secretlen, ivlen;
+
+ if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) &&
+ tx_ckm->use_count >= pktns->crypto.ctx.max_encryption &&
+ ngtcp2_conn_initiate_key_update(conn, ts) != 0) {
+ return NGTCP2_ERR_AEAD_LIMIT_REACHED;
+ }
+
+ if ((conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) ||
+ (confirmed_ts != UINT64_MAX && confirmed_ts + pto > ts)) {
+ return 0;
+ }
+
+ if (conn->crypto.key_update.new_rx_ckm ||
+ conn->crypto.key_update.new_tx_ckm) {
+ assert(conn->crypto.key_update.new_rx_ckm);
+ assert(conn->crypto.key_update.new_tx_ckm);
+ return 0;
+ }
+
+ secretlen = rx_ckm->secret.len;
+ ivlen = rx_ckm->iv.len;
+
+ rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_rx_ckm,
+ secretlen, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_tx_ckm,
+ secretlen, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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(
+ 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);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ new_rx_ckm->aead_ctx = rx_aead_ctx;
+ new_tx_ckm->aead_ctx = tx_aead_ctx;
+
+ if (!(rx_ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)) {
+ new_rx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE;
+ new_tx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE;
+ }
+
+ if (conn->crypto.key_update.old_rx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx);
+ ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem);
+ conn->crypto.key_update.old_rx_ckm = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_rotate_keys rotates keys. The current key moves to old key,
+ * and new key moves to the current key.
+ */
+static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ assert(conn->crypto.key_update.new_rx_ckm);
+ assert(conn->crypto.key_update.new_tx_ckm);
+ assert(!conn->crypto.key_update.old_rx_ckm);
+ assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING));
+
+ conn->crypto.key_update.old_rx_ckm = pktns->crypto.rx.ckm;
+
+ pktns->crypto.rx.ckm = conn->crypto.key_update.new_rx_ckm;
+ conn->crypto.key_update.new_rx_ckm = NULL;
+ pktns->crypto.rx.ckm->pkt_num = pkt_num;
+
+ assert(pktns->crypto.tx.ckm);
+
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+
+ pktns->crypto.tx.ckm = conn->crypto.key_update.new_tx_ckm;
+ conn->crypto.key_update.new_tx_ckm = NULL;
+ pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1;
+
+ conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+}
+
+/*
+ * conn_path_validation_in_progress returns nonzero if path validation
+ * against |path| is underway.
+ */
+static int conn_path_validation_in_progress(ngtcp2_conn *conn,
+ const ngtcp2_path *path) {
+ ngtcp2_pv *pv = conn->pv;
+
+ return pv && ngtcp2_path_eq(&pv->dcid.ps.path, path);
+}
+
+/*
+ * conn_recv_non_probing_pkt_on_new_path is called when non-probing
+ * packet is received via new path. It starts path validation against
+ * the new path.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONN_ID_BLOCKED
+ * No DCID is available
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ size_t dgramlen,
+ int new_cid_used,
+ ngtcp2_tstamp ts) {
+
+ ngtcp2_dcid dcid, *bound_dcid, *last;
+ ngtcp2_pv *pv;
+ int rv;
+ ngtcp2_duration pto, initial_pto, timeout;
+ int require_new_cid;
+ int local_addr_eq;
+ uint32_t remote_addr_cmp;
+ size_t len, i;
+
+ assert(conn->server);
+
+ if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)) {
+ /* If new path equals fallback path, that means connection
+ migrated back to the original path. Fallback path is
+ considered to be validated. */
+ 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->dcid.current.bytes_recv += dgramlen;
+ conn_reset_ecn_validation_state(conn);
+ rv = conn_stop_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ return 0;
+ }
+
+ remote_addr_cmp =
+ ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote);
+ local_addr_eq =
+ ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local);
+
+ /* The transport specification draft-27 says:
+ *
+ * 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.
+ */
+ require_new_cid = conn->dcid.current.cid.datalen &&
+ ((new_cid_used && remote_addr_cmp) || !local_addr_eq);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "non-probing packet was received from new remote address");
+
+ pto = conn_compute_pto(conn, &conn->pktns);
+ initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, initial_pto);
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+
+ for (i = 0; i < len; ++i) {
+ bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+ if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_CON,
+ "Found DCID which has already been bound to the new path");
+
+ 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);
+ } else {
+ last = ngtcp2_ringbuf_get(&conn->dcid.bound, len - 1);
+ ngtcp2_dcid_copy(bound_dcid, last);
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound);
+ }
+ require_new_cid = 0;
+
+ if (dcid.cid.datalen) {
+ rv = conn_call_activate_dcid(conn, &dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ break;
+ }
+ }
+
+ if (i == len) {
+ if (require_new_cid) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 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);
+
+ rv = conn_call_activate_dcid(conn, &dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ /* Use the current DCID if a remote endpoint does not change
+ DCID. */
+ ngtcp2_dcid_copy(&dcid, &conn->dcid.current);
+ dcid.bytes_sent = 0;
+ dcid.bytes_recv = 0;
+ dcid.flags &= (uint8_t)~NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ }
+ }
+
+ ngtcp2_path_copy(&dcid.ps.path, path);
+ dcid.bytes_recv += dgramlen;
+
+ rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE,
+ &conn->log, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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;
+ } 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_ecn_validation_state(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);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ conn->pv = pv;
+
+ return 0;
+}
+
+/*
+ * conn_recv_pkt_from_new_path is called when a Short 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.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONN_ID_BLOCKED
+ * No unused DCID is available
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn,
+ const ngtcp2_path *path, size_t dgramlen,
+ int path_challenge_recved,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_dcid *bound_dcid;
+ int rv;
+
+ if (pv) {
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, path)) {
+ pv->dcid.bytes_recv += dgramlen;
+ return 0;
+ }
+
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)) {
+ pv->fallback_dcid.bytes_recv += dgramlen;
+ return 0;
+ }
+ }
+
+ if (!path_challenge_recved) {
+ return 0;
+ }
+
+ rv = conn_bind_dcid(conn, &bound_dcid, path, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_path_copy(&bound_dcid->ps.path, path);
+ bound_dcid->bytes_recv += dgramlen;
+
+ return 0;
+}
+
+/*
+ * conn_recv_delayed_handshake_pkt processes the received Handshake
+ * packet which is received after handshake completed. This function
+ * does the minimal job, and its purpose is send acknowledgement of
+ * this packet to the peer. We assume that hd->type ==
+ * NGTCP2_PKT_HANDSHAKE.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Frame is badly formatted; or frame type is unknown.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_DISCARD_PKT
+ * Packet was discarded.
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_PROTO
+ * Frame that is not allowed in Handshake packet is received.
+ */
+static int
+conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
+ const ngtcp2_pkt_hd *hd, size_t pktlen,
+ const uint8_t *payload, size_t payloadlen,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ ngtcp2_max_frame mfr;
+ ngtcp2_frame *fr = &mfr.fr;
+ int rv;
+ int require_ack = 0;
+ ngtcp2_pktns *pktns;
+
+ assert(hd->type == NGTCP2_PKT_HANDSHAKE);
+
+ pktns = conn->hs_pktns;
+
+ if (payloadlen == 0) {
+ /* QUIC packet must contain at least one frame */
+ return NGTCP2_ERR_PROTO;
+ }
+
+ ngtcp2_qlog_pkt_received_start(&conn->qlog);
+
+ for (; payloadlen;) {
+ nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ payload += nread;
+ payloadlen -= (size_t)nread;
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ fr->ack.ack_delay = 0;
+ fr->ack.ack_delay_unscaled = 0;
+ break;
+ }
+
+ ngtcp2_log_rx_fr(&conn->log, hd, fr);
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (!conn->server) {
+ conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+ }
+ rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_PADDING:
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ conn_recv_connection_close(conn, &fr->connection_close);
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ case NGTCP2_FRAME_PING:
+ require_ack = 1;
+ break;
+ default:
+ return NGTCP2_ERR_PROTO;
+ }
+
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ }
+
+ ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen);
+
+ rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num, require_ack, pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns_increase_ecn_counts(pktns, pi);
+
+ if (require_ack &&
+ (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh ||
+ (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+
+ rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd->pkt_num, require_ack,
+ pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_restart_timer_on_read(conn, ts);
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ return 0;
+}
+
+/*
+ * conn_recv_pkt processes a packet contained in the buffer pointed by
+ * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets.
+ * This function only processes the first packet. |pkt_ts| is the
+ * timestamp when packet is received. |ts| should be the current
+ * time. Usually they are the same, but for buffered packets,
+ * |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns the number of bytes processed if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_DISCARD_PKT
+ * Packet was discarded because plain text header was malformed;
+ * or its payload could not be decrypted.
+ * NGTCP2_ERR_PROTO
+ * Packet is badly formatted; or 0RTT packet contains other than
+ * PADDING or STREAM frames; or other QUIC protocol violation is
+ * found.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Frame is badly formatted; or frame type is unknown.
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_STREAM_STATE
+ * Frame is received to the local stream which is not initiated.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * Frame has remote stream ID which is strictly greater than the
+ * allowed limit.
+ * NGTCP2_ERR_FLOW_CONTROL
+ * Flow control limit is violated.
+ * NGTCP2_ERR_FINAL_SIZE
+ * Frame has strictly larger end offset than it is permitted.
+ */
+static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+ ngtcp2_pkt_hd hd;
+ int rv = 0;
+ size_t hdpktlen;
+ const uint8_t *payload;
+ size_t payloadlen;
+ ngtcp2_ssize nread, nwrite;
+ ngtcp2_max_frame mfr;
+ ngtcp2_frame *fr = &mfr.fr;
+ int require_ack = 0;
+ ngtcp2_crypto_aead *aead;
+ ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx *hp_ctx;
+ ngtcp2_hp_mask hp_mask;
+ ngtcp2_decrypt decrypt;
+ ngtcp2_pktns *pktns;
+ int non_probing_pkt = 0;
+ int key_phase_bit_changed = 0;
+ int force_decrypt_failure = 0;
+ int recv_ncid = 0;
+ int new_cid_used = 0;
+ int path_challenge_recved = 0;
+
+ if (pkt[0] & NGTCP2_HEADER_FORM_BIT) {
+ nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen);
+ if (nread < 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decode long header");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ pktlen = (size_t)nread + hd.len;
+
+ /* Quoted from spec: if subsequent packets of those types include
+ a different Source Connection ID, they MUST be discarded. */
+ if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) {
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched SCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ switch (hd.type) {
+ case NGTCP2_PKT_INITIAL:
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "delayed Initial packet was discarded");
+ return (ngtcp2_ssize)pktlen;
+ case NGTCP2_PKT_HANDSHAKE:
+ if (!conn->hs_pktns) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "delayed Handshake packet was discarded");
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ pktns = conn->hs_pktns;
+ aead = &pktns->crypto.ctx.aead;
+ hp = &pktns->crypto.ctx.hp;
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
+ hp_mask = conn->callbacks.hp_mask;
+ decrypt = conn->callbacks.decrypt;
+ break;
+ case NGTCP2_PKT_0RTT:
+ if (!conn->server) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!conn->early.ckm) {
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ pktns = &conn->pktns;
+ aead = &conn->early.ctx.aead;
+ hp = &conn->early.ctx.hp;
+ ckm = conn->early.ckm;
+ hp_ctx = &conn->early.hp_ctx;
+ hp_mask = conn->callbacks.hp_mask;
+ decrypt = conn->callbacks.decrypt;
+ break;
+ default:
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet type 0x%02x was ignored", hd.type);
+ return (ngtcp2_ssize)pktlen;
+ }
+ } else {
+ nread = ngtcp2_pkt_decode_hd_short(&hd, pkt, pktlen, conn->oscid.datalen);
+ if (nread < 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decode short header");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ pktns = &conn->pktns;
+ aead = &pktns->crypto.ctx.aead;
+ hp = &pktns->crypto.ctx.hp;
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
+ hp_mask = conn->callbacks.hp_mask;
+ decrypt = conn->callbacks.decrypt;
+ }
+
+ rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
+ (size_t)nread, ckm, hp_ctx, hp_mask);
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ hdpktlen = (size_t)nwrite;
+ payload = pkt + hdpktlen;
+ payloadlen = pktlen - hdpktlen;
+
+ hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num,
+ pkt_num_bits(hd.pkt_numlen));
+ if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ if (hd.type == NGTCP2_PKT_SHORT) {
+ key_phase_bit_changed = conn_key_phase_changed(conn, &hd);
+ }
+
+ rv = conn_ensure_decrypt_buffer(conn, payloadlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (key_phase_bit_changed) {
+ assert(hd.type == NGTCP2_PKT_SHORT);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE");
+
+ if (ckm->pkt_num > hd.pkt_num) {
+ if (conn->crypto.key_update.old_rx_ckm) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "decrypting with old key");
+ ckm = conn->crypto.key_update.old_rx_ckm;
+ } else {
+ force_decrypt_failure = 1;
+ }
+ } else if (pktns->rx.max_pkt_num < hd.pkt_num) {
+ assert(ckm->pkt_num < hd.pkt_num);
+ if (!conn->crypto.key_update.new_rx_ckm) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "new key is not available");
+ force_decrypt_failure = 1;
+ } else {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "decrypting with new key");
+ ckm = conn->crypto.key_update.new_rx_ckm;
+ }
+ } else {
+ force_decrypt_failure = 1;
+ }
+ }
+
+ nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen,
+ conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num,
+ ckm, decrypt);
+
+ if (force_decrypt_failure) {
+ nwrite = NGTCP2_ERR_TLS_DECRYPT;
+ }
+
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+
+ assert(NGTCP2_ERR_TLS_DECRYPT == nwrite);
+
+ if (hd.type == NGTCP2_PKT_SHORT &&
+ ++conn->crypto.decryption_failure_count >=
+ pktns->crypto.ctx.max_decryption_failure) {
+ return NGTCP2_ERR_AEAD_LIMIT_REACHED;
+ }
+
+ if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet payload");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet payload");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = ngtcp2_pkt_verify_reserved_bits(conn->crypto.decrypt_hp_buf.base[0]);
+ if (rv != 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet has incorrect reserved bits");
+
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was discarded because of duplicated packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ payload = conn->crypto.decrypt_buf.base;
+ payloadlen = (size_t)nwrite;
+
+ if (payloadlen == 0) {
+ /* QUIC packet must contain at least one frame */
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ switch (hd.type) {
+ case NGTCP2_PKT_HANDSHAKE:
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = conn_recv_delayed_handshake_pkt(conn, pi, &hd, pktlen, payload,
+ payloadlen, pkt_ts, ts);
+ if (rv < 0) {
+ return (ngtcp2_ssize)rv;
+ }
+
+ return (ngtcp2_ssize)pktlen;
+ case NGTCP2_PKT_0RTT:
+ if (!ngtcp2_cid_eq(&conn->rcid, &hd.dcid)) {
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ }
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+ } else {
+ rv = conn_verify_dcid(conn, &new_cid_used, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "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);
+
+ for (; payloadlen;) {
+ nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ if (nread < 0) {
+ return nread;
+ }
+
+ payload += nread;
+ payloadlen -= (size_t)nread;
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if ((hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) &&
+ hd.type == NGTCP2_PKT_0RTT) {
+ return NGTCP2_ERR_PROTO;
+ }
+ assign_recved_ack_delay_unscaled(
+ &fr->ack, conn->remote.transport_params.ack_delay_exponent);
+ break;
+ }
+
+ ngtcp2_log_rx_fr(&conn->log, &hd, fr);
+
+ if (hd.type == NGTCP2_PKT_0RTT) {
+ switch (fr->type) {
+ case NGTCP2_FRAME_PADDING:
+ case NGTCP2_FRAME_PING:
+ case NGTCP2_FRAME_RESET_STREAM:
+ case NGTCP2_FRAME_STOP_SENDING:
+ case NGTCP2_FRAME_STREAM:
+ case NGTCP2_FRAME_MAX_DATA:
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ break;
+ default:
+ return NGTCP2_ERR_PROTO;
+ }
+ }
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ case NGTCP2_FRAME_PADDING:
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ break;
+ default:
+ require_ack = 1;
+ }
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (!conn->server) {
+ conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+ }
+ rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STREAM:
+ rv = conn_recv_stream(conn, &fr->stream);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ rv = conn_recv_crypto(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION,
+ &pktns->crypto.strm, &fr->crypto);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_RESET_STREAM:
+ rv = conn_recv_reset_stream(conn, &fr->reset_stream);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STOP_SENDING:
+ rv = conn_recv_stop_sending(conn, &fr->stop_sending);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ rv = conn_recv_max_stream_data(conn, &fr->max_stream_data);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_MAX_DATA:
+ conn_recv_max_data(conn, &fr->max_data);
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ rv = conn_recv_max_streams(conn, &fr->max_streams);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ conn_recv_connection_close(conn, &fr->connection_close);
+ break;
+ case NGTCP2_FRAME_PING:
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ conn_recv_path_challenge(conn, path, &fr->path_challenge);
+ path_challenge_recved = 1;
+ break;
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ rv = conn_recv_path_response(conn, &fr->path_response, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ rv = conn_recv_new_connection_id(conn, &fr->new_connection_id);
+ if (rv != 0) {
+ return rv;
+ }
+ recv_ncid = 1;
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ rv = conn_recv_retire_connection_id(conn, &hd, &fr->retire_connection_id,
+ ts);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ rv = conn_recv_new_token(conn, &fr->new_token);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ rv = conn_recv_handshake_done(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ rv = conn_recv_streams_blocked_bidi(conn, &fr->streams_blocked);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ rv = conn_recv_streams_blocked_uni(conn, &fr->streams_blocked);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ /* TODO Not implemented yet */
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ if ((uint64_t)nread >
+ conn->local.transport_params.max_datagram_frame_size) {
+ return NGTCP2_ERR_PROTO;
+ }
+ rv = conn_recv_datagram(conn, &fr->datagram);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ }
+
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ }
+
+ ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen);
+
+ if (recv_ncid) {
+ rv = conn_post_process_recv_new_connection_id(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (conn->server && hd.type == NGTCP2_PKT_SHORT &&
+ !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)) {
+ rv = conn_recv_non_probing_pkt_on_new_path(conn, path, dgramlen,
+ new_cid_used, ts);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ /* DCID is not available. Just continue. */
+ assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv);
+ }
+ } else {
+ rv = conn_recv_pkt_from_new_path(conn, path, dgramlen,
+ path_challenge_recved, ts);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ /* DCID is not available. Just continue. */
+ assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv);
+ }
+ }
+ }
+
+ if (hd.type == NGTCP2_PKT_SHORT) {
+ 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);
+ } else if (ckm->pkt_num > hd.pkt_num) {
+ ckm->pkt_num = hd.pkt_num;
+ }
+
+ if (conn->server && conn->early.ckm &&
+ conn->early.discard_started_ts == UINT64_MAX) {
+ conn->early.discard_started_ts = ts;
+ }
+ }
+
+ rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns_increase_ecn_counts(pktns, pi);
+
+ if (require_ack &&
+ (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh ||
+ (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+
+ rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack,
+ pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_restart_timer_on_read(conn, ts);
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING
+ : (ngtcp2_ssize)pktlen;
+}
+
+/*
+ * conn_process_buffered_protected_pkt processes buffered 0RTT or
+ * Short packets.
+ *
+ * This function returns 0 if it succeeds, or the same negative error
+ * codes from conn_recv_pkt.
+ */
+static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_chain **ppc, *next;
+ int rv;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "processing buffered protected packet");
+
+ for (ppc = &pktns->rx.buffed_pkts; *ppc;) {
+ next = (*ppc)->next;
+ nread = conn_recv_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi, (*ppc)->pkt,
+ (*ppc)->pktlen, (*ppc)->dgramlen, (*ppc)->ts, ts);
+ if (nread < 0 && !ngtcp2_err_is_fatal((int)nread) &&
+ nread != NGTCP2_ERR_DRAINING) {
+ /* TODO We don't know this is the first QUIC packet in a
+ datagram. */
+ rv = conn_on_stateless_reset(conn, &(*ppc)->path.path, (*ppc)->pkt,
+ (*ppc)->pktlen);
+ if (rv == 0) {
+ ngtcp2_pkt_chain_del(*ppc, conn->mem);
+ *ppc = next;
+ return NGTCP2_ERR_DRAINING;
+ }
+ }
+
+ ngtcp2_pkt_chain_del(*ppc, conn->mem);
+ *ppc = next;
+ if (nread < 0) {
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ continue;
+ }
+ return (int)nread;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_process_buffered_handshake_pkt processes buffered Handshake
+ * packets.
+ *
+ * This function returns 0 if it succeeds, or the same negative error
+ * codes from conn_recv_handshake_pkt.
+ */
+static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pktns *pktns = conn->hs_pktns;
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_chain **ppc, *next;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "processing buffered handshake packet");
+
+ for (ppc = &pktns->rx.buffed_pkts; *ppc;) {
+ next = (*ppc)->next;
+ nread = conn_recv_handshake_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi,
+ (*ppc)->pkt, (*ppc)->pktlen,
+ (*ppc)->dgramlen, (*ppc)->ts, ts);
+ ngtcp2_pkt_chain_del(*ppc, conn->mem);
+ *ppc = next;
+ if (nread < 0) {
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ continue;
+ }
+ return (int)nread;
+ }
+ }
+
+ return 0;
+}
+
+static void conn_sync_stream_id_limit(ngtcp2_conn *conn) {
+ ngtcp2_transport_params *params = &conn->remote.transport_params;
+
+ conn->local.bidi.max_streams = params->initial_max_streams_bidi;
+ conn->local.uni.max_streams = params->initial_max_streams_uni;
+}
+
+/*
+ * conn_handshake_completed is called once cryptographic handshake has
+ * completed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ */
+static int conn_handshake_completed(ngtcp2_conn *conn) {
+ int rv;
+
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED;
+
+ rv = conn_call_handshake_completed(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->local.bidi.max_streams > 0) {
+ rv = conn_call_extend_max_local_streams_bidi(conn,
+ conn->local.bidi.max_streams);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ if (conn->local.uni.max_streams > 0) {
+ rv = conn_call_extend_max_local_streams_uni(conn,
+ conn->local.uni.max_streams);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_cpkt processes compound packet after handshake. The
+ * buffer pointed by |pkt| might contain multiple packets. The Short
+ * packet must be the last one because it does not have payload length
+ * field.
+ *
+ * This function returns 0 if it succeeds, or the same negative error
+ * codes from conn_recv_pkt except for NGTCP2_ERR_DISCARD_PKT.
+ */
+static int conn_recv_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;
+ int rv;
+ const uint8_t *origpkt = pkt;
+ size_t dgramlen = pktlen;
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn->dcid.current.bytes_recv += dgramlen;
+ }
+
+ while (pktlen) {
+ nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts);
+ if (nread < 0) {
+ if (ngtcp2_err_is_fatal((int)nread)) {
+ return (int)nread;
+ }
+
+ if (nread == NGTCP2_ERR_DRAINING) {
+ return NGTCP2_ERR_DRAINING;
+ }
+
+ if (origpkt == pkt) {
+ rv = conn_on_stateless_reset(conn, path, origpkt, dgramlen);
+ if (rv == 0) {
+ return NGTCP2_ERR_DRAINING;
+ }
+ }
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ return 0;
+ }
+ return (int)nread;
+ }
+
+ assert(pktlen >= (size_t)nread);
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "read packet %td left %zu", nread, pktlen);
+ }
+
+ return 0;
+}
+
+/*
+ * conn_is_retired_path returns nonzero if |path| is included in
+ * 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);
+ ngtcp2_dcid *dcid;
+
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+ if (ngtcp2_path_eq(&dcid->ps.path, path)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for
+ * transmission.
+ */
+static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_frame_chain *nfrc;
+ int rv;
+
+ assert(conn->server);
+
+ rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE;
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+
+ return 0;
+}
+
+/**
+ * @function
+ *
+ * `conn_read_handshake` performs QUIC cryptographic handshake by
+ * reading given data. |pkt| points to the buffer to read and
+ * |pktlen| is the length of the buffer. |path| is the network path.
+ *
+ * This function returns 0 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) {
+ int rv;
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ /* TODO Better to log something when we ignore input */
+ return 0;
+ case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
+ rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (rv < 0) {
+ return rv;
+ }
+
+ if (conn->state == NGTCP2_CS_CLIENT_INITIAL) {
+ /* Retry packet was received */
+ return 0;
+ }
+
+ assert(conn->hs_pktns);
+
+ if (conn->hs_pktns->crypto.rx.ckm && conn->in_pktns) {
+ rv = conn_process_buffered_handshake_pkt(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if ((conn->flags & (NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) ==
+ NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) {
+ rv = conn_handshake_completed(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+ case NGTCP2_CS_SERVER_INITIAL:
+ rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (rv < 0) {
+ return rv;
+ }
+
+ /*
+ * Client ServerHello might not fit into single Initial packet
+ * (e.g., resuming session with client authentication). If we get
+ * Client Initial which does not increase offset or it is 0RTT
+ * packet buffered, perform address validation in order to buffer
+ * validated data only.
+ */
+ if (ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0) {
+ if (conn->in_pktns->crypto.strm.rx.rob &&
+ 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 NGTCP2_ERR_RETRY;
+ }
+ if (conn->in_pktns->rx.buffed_pkts) {
+ /* 0RTT is buffered, force retry */
+ return NGTCP2_ERR_RETRY;
+ }
+ /* If neither CRYPTO frame nor 0RTT packet is processed, just
+ drop connection. */
+ return NGTCP2_ERR_DROP_CONN;
+ }
+
+ /* Process re-ordered 0-RTT packets which arrived before Initial
+ packet. */
+ if (conn->early.ckm) {
+ assert(conn->in_pktns);
+
+ rv = conn_process_buffered_protected_pkt(conn, conn->in_pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (rv < 0) {
+ return rv;
+ }
+
+ if (conn->hs_pktns->crypto.rx.ckm) {
+ rv = conn_process_buffered_handshake_pkt(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (conn->hs_pktns->rx.max_pkt_num != -1) {
+ conn_discard_initial_state(conn, ts);
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ /* 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
+ ack-eliciting Initial or Handshake packets, it should resend
+ it if timer fired but timer is not armed in this case. So
+ instead of resending Initial/Handshake packets, if server has
+ 1RTT data to send, it might send them and then might hit
+ amplification limit again until it hits stream data limit.
+ Initial/Handshake data is not resent. In order to avoid this
+ situation, try to arm loss detection and check the expiry
+ here so that on next write call, we can resend
+ Initial/Handshake first. */
+ if (conn->cstat.loss_detection_timer == UINT64_MAX) {
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) {
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
+
+ rv = conn_handshake_completed(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ conn->state = NGTCP2_CS_POST_HANDSHAKE;
+
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_discard_handshake_state(conn, ts);
+
+ rv = conn_enqueue_handshake_done(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;
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ default:
+ return 0;
+ }
+}
+
+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 rv = 0;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu",
+ pktlen);
+
+ if (pktlen == 0) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ /* client does not expect a packet from unknown path. */
+ if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
+ (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)) &&
+ !conn_is_retired_path(conn, path)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "ignore packet from unknown path");
+ return 0;
+ }
+
+ 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);
+ case NGTCP2_CS_SERVER_INITIAL:
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED:
+ if (!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "ignore packet from unknown path during handshake");
+
+ if (conn->state == NGTCP2_CS_SERVER_INITIAL &&
+ ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0 &&
+ (!conn->in_pktns->crypto.strm.rx.rob ||
+ !ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob))) {
+ return NGTCP2_ERR_DROP_CONN;
+ }
+
+ return 0;
+ }
+ return conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ case NGTCP2_CS_POST_HANDSHAKE:
+ rv = conn_prepare_key_update(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts);
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+/*
+ * conn_check_pkt_num_exhausted returns nonzero if packet number is
+ * exhausted in at least one of packet number space.
+ */
+static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+
+ return (in_pktns && in_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) ||
+ (hs_pktns && hs_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) ||
+ conn->pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM;
+}
+
+/*
+ * conn_retransmit_retry_early retransmits 0RTT packet after Retry is
+ * received from server.
+ */
+static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_tstamp ts) {
+ return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT,
+ NGTCP2_WRITE_PKT_FLAG_NONE, ts);
+}
+
+/*
+ * conn_handshake_probe_left returns nonzero if there are probe
+ * packets to be sent for Initial or Handshake packet number space
+ * left.
+ */
+static int conn_handshake_probe_left(ngtcp2_conn *conn) {
+ return (conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) ||
+ conn->hs_pktns->rtb.probe_pkt_left;
+}
+
+/*
+ * 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.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * NGTCP2_ERR_PKT_NUM_EXHAUSTED
+ * Packet number is exhausted.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM
+ * Required transport parameter is missing.
+ * NGTCP2_CS_CLOSING
+ * Connection is in closing state.
+ * NGTCP2_CS_DRAINING
+ * Connection is in draining state.
+ *
+ * In addition to the above negative error codes, the same error codes
+ * from conn_recv_pkt may also be returned.
+ */
+static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ size_t early_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;
+ ngtcp2_dcid *dcid;
+ ngtcp2_preferred_addr *paddr;
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ pending_early_datalen = conn_retry_early_payloadlen(conn);
+ if (pending_early_datalen) {
+ early_datalen = pending_early_datalen;
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) {
+ nwrite =
+ conn_write_client_initial(conn, pi, dest, destlen, early_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);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ }
+
+ if (pending_early_datalen) {
+ early_spktlen = conn_retransmit_retry_early(conn, pi, dest + nwrite,
+ destlen - (size_t)nwrite, ts);
+
+ if (early_spktlen < 0) {
+ assert(ngtcp2_err_is_fatal((int)early_spktlen));
+ return early_spktlen;
+ }
+ }
+
+ conn->state = NGTCP2_CS_CLIENT_WAIT_HANDSHAKE;
+
+ res = nwrite + early_spktlen;
+
+ return res;
+ case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
+ if (!conn_handshake_probe_left(conn) && conn_cwnd_is_zero(conn)) {
+ destlen = 0;
+ } else {
+ 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;
+ }
+ }
+
+ nwrite =
+ conn_write_handshake_pkts(conn, pi, dest, destlen, early_datalen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
+ nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+ }
+
+ if (res == 0) {
+ nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ res = nwrite;
+ }
+
+ return res;
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
+
+ conn->state = NGTCP2_CS_POST_HANDSHAKE;
+
+ if (conn->remote.transport_params.preferred_address_present) {
+ assert(!ngtcp2_ringbuf_full(&conn->dcid.unused));
+
+ paddr = &conn->remote.transport_params.preferred_address;
+ dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused);
+ ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token);
+
+ rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1);
+ if (rv != 0) {
+ return (ngtcp2_ssize)rv;
+ }
+ }
+
+ 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));
+ }
+
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_process_early_rtb(conn);
+
+ rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts);
+ if (rv != 0) {
+ return (ngtcp2_ssize)rv;
+ }
+
+ return res;
+ case NGTCP2_CS_SERVER_INITIAL:
+ nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen,
+ /* early_datalen = */ 0, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (nwrite) {
+ conn->state = NGTCP2_CS_SERVER_WAIT_HANDSHAKE;
+ }
+
+ 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 (res == 0) {
+ nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+ dest += nwrite;
+ origlen -= (size_t)nwrite;
+ }
+
+ return res;
+ }
+
+ return 0;
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * @function
+ *
+ * `conn_client_write_handshake` writes client side handshake data and
+ * 0RTT packet.
+ *
+ * In order to send STREAM data in 0RTT packet, specify
+ * |vmsg|->stream. |vmsg|->stream.strm, |vmsg|->stream.fin,
+ * |vmsg|->stream.data, and |vmsg|->stream.datacnt are stream to which
+ * 0-RTT data is sent, whether it is a last data chunk in this stream,
+ * a vector of 0-RTT data, and its number of elements respectively.
+ * The amount of 0RTT data sent is assigned to
+ * *|vmsg|->stream.pdatalen. If no data is sent, -1 is assigned.
+ * Note that 0 length STREAM frame is allowed in QUIC, so 0 might be
+ * assigned to *|vmsg|->stream.pdatalen.
+ *
+ * This function returns 0 if it cannot write any frame because buffer
+ * is too small, or packet is congestion limited. Application should
+ * keep reading and wait for congestion window to grow.
+ *
+ * This function returns the number of bytes written to the buffer
+ * pointed by |dest| if it succeeds, or one of the following negative
+ * error codes: (TBD).
+ */
+static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg,
+ ngtcp2_tstamp ts) {
+ 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;
+ uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+
+ 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)) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt);
+ send_stream =
+ conn_retry_early_payloadlen(conn) == 0 &&
+ /* 0 length STREAM frame is allowed */
+ (datalen == 0 ||
+ (datalen > 0 &&
+ (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;
+
+ if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ } else {
+ vmsg = NULL;
+ }
+ 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;
+
+ if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ } else {
+ vmsg = NULL;
+ }
+ break;
+ }
+ }
+
+ if (!ppe_pending) {
+ was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL;
+ spktlen = conn_write_handshake(conn, pi, dest, destlen, early_datalen, ts);
+
+ if (spktlen < 0) {
+ return spktlen;
+ }
+
+ if (conn->pktns.crypto.tx.ckm || !conn->early.ckm ||
+ (!send_stream && !send_datagram)) {
+ return spktlen;
+ }
+ } else {
+ assert(!conn->pktns.crypto.tx.ckm);
+ assert(conn->early.ckm);
+
+ was_client_initial = conn->pkt.was_client_initial;
+ spktlen = conn->pkt.hs_spktlen;
+ }
+
+ /* If spktlen > 0, we are making a compound packet. If Initial
+ packet is written, we have to pad bytes to 0-RTT packet. */
+
+ if (spktlen && was_client_initial) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+
+ dest += spktlen;
+ destlen -= (size_t)spktlen;
+
+ if (conn_cwnd_is_zero(conn)) {
+ return spktlen;
+ }
+
+ early_spktlen = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_0RTT,
+ wflags, ts);
+
+ if (early_spktlen < 0) {
+ switch (early_spktlen) {
+ 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;
+ }
+ return early_spktlen;
+ }
+
+ return spktlen + early_spktlen;
+}
+
+void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) {
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED;
+ if (conn->server) {
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED;
+ }
+}
+
+int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) {
+ return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+ (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED);
+}
+
+int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr,
+ int64_t pkt_num, int active_ack, ngtcp2_tstamp ts) {
+ int rv;
+ (void)conn;
+
+ rv = ngtcp2_acktr_add(acktr, pkt_num, active_ack, ts);
+ if (rv != 0) {
+ assert(rv != NGTCP2_ERR_INVALID_ARGUMENT);
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) {
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_hd hd, *p;
+
+ if (dest) {
+ p = dest;
+ } else {
+ p = &hd;
+ }
+
+ if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) {
+ return -1;
+ }
+
+ nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen);
+ if (nread < 0) {
+ return -1;
+ }
+
+ 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;
+ default:
+ return -1;
+ }
+
+ if (p->version != NGTCP2_PROTO_VER_V1 &&
+ (p->version < NGTCP2_PROTO_VER_DRAFT_MIN ||
+ NGTCP2_PROTO_VER_DRAFT_MAX < p->version)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_initial_key(
+ ngtcp2_conn *conn, 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) {
+ ngtcp2_pktns *pktns = conn->in_pktns;
+ int rv;
+
+ assert(pktns);
+
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx);
+ pktns->crypto.rx.hp_ctx.native_handle = NULL;
+
+ if (pktns->crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
+ pktns->crypto.rx.ckm = NULL;
+ }
+
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx);
+ pktns->crypto.tx.hp_ctx.native_handle = NULL;
+
+ if (pktns->crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+ pktns->crypto.tx.ckm = NULL;
+ }
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, NULL, rx_iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.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. */
+ pktns->crypto.rx.ckm->aead_ctx = *rx_aead_ctx;
+ pktns->crypto.rx.hp_ctx = *rx_hp_ctx;
+ pktns->crypto.tx.ckm->aead_ctx = *tx_aead_ctx;
+ pktns->crypto.tx.hp_ctx = *tx_hp_ctx;
+
+ 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(pktns);
+ assert(!pktns->crypto.rx.hp_ctx.native_handle);
+ assert(!pktns->crypto.rx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, aead_ctx, iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns->crypto.rx.hp_ctx = *hp_ctx;
+
+ return 0;
+}
+
+int ngtcp2_conn_install_tx_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(pktns);
+ assert(!pktns->crypto.tx.hp_ctx.native_handle);
+ assert(!pktns->crypto.tx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, aead_ctx, iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns->crypto.tx.hp_ctx = *hp_ctx;
+
+ if (conn->server) {
+ return ngtcp2_conn_commit_local_transport_params(conn);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_early_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) {
+ int rv;
+
+ assert(!conn->early.hp_ctx.native_handle);
+ assert(!conn->early.ckm);
+
+ rv = ngtcp2_crypto_km_new(&conn->early.ckm, NULL, 0, aead_ctx, iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->early.hp_ctx = *hp_ctx;
+
+ return 0;
+}
+
+int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret,
+ size_t secretlen,
+ 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->pktns;
+ int rv;
+
+ assert(!pktns->crypto.rx.hp_ctx.native_handle);
+ assert(!pktns->crypto.rx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, secret, secretlen, aead_ctx,
+ iv, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret,
+ size_t secretlen,
+ 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->pktns;
+ int rv;
+
+ assert(!pktns->crypto.tx.hp_ctx.native_handle);
+ assert(!pktns->crypto.tx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, secret, secretlen, aead_ctx,
+ iv, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ 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;
+ } else if (conn->early.ckm) {
+ conn_discard_early_key(conn);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+
+ assert(conn->state == NGTCP2_CS_POST_HANDSHAKE);
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) ||
+ (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) ||
+ !conn->crypto.key_update.new_tx_ckm ||
+ !conn->crypto.key_update.new_rx_ckm ||
+ (confirmed_ts != UINT64_MAX && confirmed_ts + 3 * pto > ts)) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM);
+
+ return 0;
+}
+
+ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn) {
+ return conn->cstat.loss_detection_timer;
+}
+
+ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) {
+ ngtcp2_tstamp res = UINT64_MAX, t;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+ ngtcp2_scid *scid;
+ ngtcp2_dcid *dcid;
+
+ if (conn->pv) {
+ res = ngtcp2_pv_next_expiry(conn->pv);
+ }
+
+ 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 (ngtcp2_ringbuf_len(&conn->dcid.retired)) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0);
+ res = ngtcp2_min(res, dcid->ts_retired + pto);
+ }
+
+ if (conn->server && conn->early.ckm &&
+ conn->early.discard_started_ts != UINT64_MAX) {
+ t = conn->early.discard_started_ts + 3 * pto;
+ res = ngtcp2_min(res, t);
+ }
+
+ return res;
+}
+
+ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) {
+ ngtcp2_acktr *acktr = &conn->pktns.acktr;
+
+ if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+ acktr->first_unacked_ts != UINT64_MAX) {
+ return acktr->first_unacked_ts + conn_compute_ack_delay(conn);
+ }
+ return UINT64_MAX;
+}
+
+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 res = ngtcp2_min(t1, t2);
+ res = ngtcp2_min(res, t3);
+ return ngtcp2_min(res, t4);
+}
+
+int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+
+ ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts);
+
+ ngtcp2_conn_remove_lost_pkt(conn, ts);
+
+ if (conn->pv) {
+ ngtcp2_pv_cancel_expired_timer(conn->pv, ts);
+ }
+
+ if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) {
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = conn_remove_retired_connection_id(conn, pto, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->server && conn->early.ckm &&
+ conn->early.discard_started_ts != UINT64_MAX) {
+ if (conn->early.discard_started_ts + 3 * pto <= ts) {
+ conn_discard_early_key(conn);
+ }
+ }
+
+ return 0;
+}
+
+static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr,
+ ngtcp2_tstamp ts) {
+ if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+ acktr->first_unacked_ts <= ts) {
+ acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER;
+ }
+}
+
+void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ if (conn->in_pktns) {
+ acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts);
+ }
+ if (conn->hs_pktns) {
+ acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts);
+ }
+ acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts);
+}
+
+ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) {
+ ngtcp2_tstamp res = UINT64_MAX, ts;
+
+ if (conn->in_pktns) {
+ ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb);
+ if (ts != UINT64_MAX) {
+ ts += conn_compute_pto(conn, conn->in_pktns);
+ res = ngtcp2_min(res, ts);
+ }
+ }
+
+ if (conn->hs_pktns) {
+ ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb);
+ if (ts != UINT64_MAX) {
+ ts += conn_compute_pto(conn, conn->hs_pktns);
+ res = ngtcp2_min(res, ts);
+ }
+ }
+
+ ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb);
+ if (ts != UINT64_MAX) {
+ ts += conn_compute_pto(conn, &conn->pktns);
+ res = ngtcp2_min(res, ts);
+ }
+
+ return res;
+}
+
+void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_duration pto;
+
+ if (conn->in_pktns) {
+ pto = conn_compute_pto(conn, conn->in_pktns);
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts);
+ }
+ if (conn->hs_pktns) {
+ pto = conn_compute_pto(conn, conn->hs_pktns);
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts);
+ }
+ pto = conn_compute_pto(conn, &conn->pktns);
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts);
+}
+
+/*
+ * 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.
+ */
+static int
+conn_client_validate_transport_params(ngtcp2_conn *conn,
+ const ngtcp2_transport_params *params) {
+ if (!ngtcp2_cid_eq(&conn->rcid, &params->original_dcid)) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) {
+ if (!params->retry_scid_present) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+ if (!ngtcp2_cid_eq(&conn->retry_scid, &params->retry_scid)) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+ } else if (params->retry_scid_present) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (params->preferred_address_present &&
+ conn->dcid.current.cid.datalen == 0) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ return 0;
+}
+
+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));
+
+ /* Assume that ngtcp2_decode_transport_params sets default value if
+ active_connection_id_limit is omitted. */
+ if (params->active_connection_id_limit <
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ /* We assume that conn->dcid.current.cid is still the initial one.
+ This requires that transport parameter must be fed into
+ ngtcp2_conn as early as possible. */
+ if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &params->initial_scid)) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (params->max_udp_payload_size < 1200) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (!conn->server) {
+ rv = conn_client_validate_transport_params(conn, params);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ ngtcp2_log_remote_tp(&conn->log,
+ conn->server
+ ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
+ : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
+ params);
+
+ ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server,
+ NGTCP2_QLOG_SIDE_REMOTE);
+
+ if ((conn->server && conn->pktns.crypto.tx.ckm) ||
+ (!conn->server && conn->pktns.crypto.rx.ckm)) {
+ conn->remote.transport_params = *params;
+ conn_sync_stream_id_limit(conn);
+ conn->tx.max_offset = conn->remote.transport_params.initial_max_data;
+ } else {
+ conn->remote.pending_transport_params = *params;
+ }
+
+ conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED;
+
+ 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;
+ }
+}
+
+void ngtcp2_conn_set_early_remote_transport_params(
+ ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
+ ngtcp2_transport_params *p = &conn->remote.transport_params;
+
+ assert(!conn->server);
+
+ memset(p, 0, sizeof(*p));
+
+ p->initial_max_streams_bidi = params->initial_max_streams_bidi;
+ p->initial_max_streams_uni = params->initial_max_streams_uni;
+ p->initial_max_stream_data_bidi_local =
+ params->initial_max_stream_data_bidi_local;
+ p->initial_max_stream_data_bidi_remote =
+ 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->max_datagram_frame_size = params->max_datagram_frame_size;
+
+ conn_sync_stream_id_limit(conn);
+
+ conn->tx.max_offset = p->initial_max_data;
+
+ ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, p, conn->server,
+ NGTCP2_QLOG_SIDE_REMOTE);
+}
+
+int ngtcp2_conn_set_local_transport_params(
+ ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
+ assert(conn->server);
+ assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE);
+
+ if (conn->hs_pktns == NULL || conn->hs_pktns->crypto.tx.ckm) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ conn->local.transport_params = *params;
+
+ return 0;
+}
+
+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));
+
+ if (params->active_connection_id_limit == 0) {
+ params->active_connection_id_limit =
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ }
+
+ params->initial_scid = conn->oscid;
+
+ if (conn->oscid.datalen == 0) {
+ 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 (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,
+ 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;
+ }
+ }
+
+ conn->rx.window = conn->rx.unsent_max_offset = conn->rx.max_offset =
+ params->initial_max_data;
+ conn->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi;
+ conn->remote.bidi.max_streams = params->initial_max_streams_bidi;
+ conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni;
+ conn->remote.uni.max_streams = params->initial_max_streams_uni;
+
+ 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;
+}
+
+int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
+ void *stream_user_data) {
+ int rv;
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_conn_get_streams_bidi_left(conn) == 0) {
+ return NGTCP2_ERR_STREAM_ID_BLOCKED;
+ }
+
+ strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ if (strm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ 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);
+ return rv;
+ }
+
+ *pstream_id = conn->local.bidi.next_stream_id;
+ conn->local.bidi.next_stream_id += 4;
+
+ return 0;
+}
+
+int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id,
+ void *stream_user_data) {
+ int rv;
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_conn_get_streams_uni_left(conn) == 0) {
+ return NGTCP2_ERR_STREAM_ID_BLOCKED;
+ }
+
+ strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+ if (strm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ 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);
+ return rv;
+ }
+ ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD);
+
+ *pstream_id = conn->local.uni.next_stream_id;
+ conn->local.uni.next_stream_id += 4;
+
+ return 0;
+}
+
+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);
+}
+
+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_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);
+}
+
+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_vmsg vmsg, *pvmsg;
+ ngtcp2_strm *strm;
+
+ if (pdatalen) {
+ *pdatalen = -1;
+ }
+
+ if (stream_id != -1) {
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) {
+ return NGTCP2_ERR_STREAM_SHUT_WR;
+ }
+
+ vmsg.type = NGTCP2_VMSG_TYPE_STREAM;
+ vmsg.stream.strm = strm;
+ vmsg.stream.flags = flags;
+ vmsg.stream.data = datav;
+ vmsg.stream.datacnt = datavcnt;
+ vmsg.stream.pdatalen = pdatalen;
+
+ pvmsg = &vmsg;
+ } else {
+ pvmsg = NULL;
+ }
+
+ return ngtcp2_conn_write_vmsg(conn, path, 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_vmsg vmsg;
+
+ if (paccepted) {
+ *paccepted = 0;
+ }
+
+ if (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))) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM;
+ 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);
+}
+
+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) {
+ ngtcp2_ssize nwrite;
+ ngtcp2_pktns *pktns = &conn->pktns;
+ size_t origlen = destlen;
+ int rv;
+ uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+ ngtcp2_ssize res = 0;
+ size_t server_tx_left;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ if (!ppe_pending && pi) {
+ pi->ecn = NGTCP2_ECN_NOT_ECT;
+ }
+
+ 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) {
+ return nwrite;
+ }
+ if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
+ return nwrite;
+ }
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ /* Break here so that we can coalesces Short packets. */
+ break;
+ case NGTCP2_CS_SERVER_INITIAL:
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED:
+ 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);
+ }
+
+ nwrite = conn_write_handshake(conn, pi, dest, destlen, 0, 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->state != NGTCP2_CS_POST_HANDSHAKE &&
+ conn->pktns.crypto.tx.ckm == NULL) {
+ return res;
+ }
+ break;
+ case NGTCP2_CS_POST_HANDSHAKE:
+ break;
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ default:
+ return 0;
+ }
+
+ assert(pktns->crypto.tx.ckm);
+
+ if (conn_check_pkt_num_exhausted(conn)) {
+ return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
+ }
+
+ if (vmsg) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ppe_pending) {
+ res = conn->pkt.hs_spktlen;
+ conn->pkt.hs_spktlen = 0;
+ /* 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,
+ wflags, ts);
+ goto fin;
+ } else {
+ if (conn->state == NGTCP2_CS_POST_HANDSHAKE) {
+ rv = conn_prepare_key_update(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ 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 (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");
+ conn->cstat.loss_detection_timer = UINT64_MAX;
+ conn->cstat.pto_count = 0;
+ }
+ }
+ }
+ }
+
+ if (res == 0) {
+ if (conn_handshake_remnants_left(conn)) {
+ if (conn_handshake_probe_left(conn)) {
+ destlen = origlen;
+ }
+ nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, 0, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ if (nwrite > 0) {
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+ }
+ }
+
+ if (conn->pktns.rtb.probe_pkt_left) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "transmit probe pkt left=%zu",
+ conn->pktns.rtb.probe_pkt_left);
+
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+ wflags, ts);
+
+ goto fin;
+ }
+
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+ wflags, ts);
+ if (nwrite) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ goto fin;
+ }
+
+ if (res == 0) {
+ nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_SHORT, ts);
+ }
+
+fin:
+ conn->pkt.hs_spktlen = 0;
+
+ if (nwrite >= 0) {
+ res += nwrite;
+ return res;
+ }
+ /* NGTCP2_CONN_FLAG_PPE_PENDING is set in conn_write_pkt above.
+ ppe_pending cannot be used here. */
+ if (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) {
+ conn->pkt.hs_spktlen = res;
+ }
+
+ return nwrite;
+}
+
+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) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_ssize res = 0, nwrite;
+ ngtcp2_frame fr;
+
+ 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;
+
+ 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);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ res += nwrite;
+ }
+
+ if (pkt_type != NGTCP2_PKT_HANDSHAKE && hs_pktns &&
+ 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);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ res += nwrite;
+ }
+ }
+
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, pkt_type, &conn->dcid.current.cid, &fr,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (res == 0) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ return res;
+}
+
+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) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ uint8_t pkt_type;
+ ngtcp2_ssize nwrite;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ if (conn_check_pkt_num_exhausted(conn)) {
+ return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
+ }
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ case NGTCP2_CS_CLOSING:
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_INVALID_STATE;
+ default:
+ break;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ if (pi) {
+ pi->ecn = NGTCP2_ECN_NOT_ECT;
+ }
+
+ if (conn->state == NGTCP2_CS_POST_HANDSHAKE ||
+ (conn->server && conn->pktns.crypto.tx.ckm)) {
+ pkt_type = NGTCP2_PKT_SHORT;
+ } else if (hs_pktns && hs_pktns->crypto.tx.ckm) {
+ pkt_type = NGTCP2_PKT_HANDSHAKE;
+ } else if (in_pktns && in_pktns->crypto.tx.ckm) {
+ pkt_type = NGTCP2_PKT_INITIAL;
+ } else {
+ /* This branch is taken if server has not read any Initial packet
+ from client. */
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ nwrite = conn_write_connection_close(conn, pi, dest, destlen, pkt_type,
+ error_code, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ conn->state = NGTCP2_CS_CLOSING;
+
+ return nwrite;
+}
+
+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_ssize nwrite;
+ ngtcp2_ssize res = 0;
+ ngtcp2_frame fr;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ if (conn_check_pkt_num_exhausted(conn)) {
+ return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
+ }
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ case NGTCP2_CS_CLOSING:
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_INVALID_STATE;
+ default:
+ break;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ if (pi) {
+ pi->ecn = NGTCP2_ECN_NOT_ECT;
+ }
+
+ 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);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+
+ if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
+ assert(res);
+
+ if (!conn->server || !conn->pktns.crypto.tx.ckm) {
+ return res;
+ }
+ }
+
+ assert(conn->pktns.crypto.tx.ckm);
+
+ 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;
+
+ 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);
+
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (res == 0) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ conn->state = NGTCP2_CS_CLOSING;
+
+ return res;
+}
+
+int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) {
+ return conn->state == NGTCP2_CS_CLOSING;
+}
+
+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 rv;
+
+ if (!strm->app_error_code) {
+ app_error_code = strm->app_error_code;
+ }
+
+ rv = ngtcp2_map_remove(&conn->strms, strm->me.key);
+ if (rv != 0) {
+ assert(rv != NGTCP2_ERR_INVALID_ARGUMENT);
+ return rv;
+ }
+
+ rv = conn_call_stream_close(conn, strm, app_error_code);
+ if (rv != 0) {
+ goto fin;
+ }
+
+ if (ngtcp2_strm_is_tx_queued(strm)) {
+ ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe);
+ }
+
+fin:
+ ngtcp2_strm_free(strm);
+ ngtcp2_mem_free(conn->mem, strm);
+
+ return rv;
+}
+
+int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ 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);
+ }
+ return 0;
+}
+
+/*
+ * conn_shutdown_stream_write closes send stream with error code
+ * |app_error_code|. RESET_STREAM frame is scheduled.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+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) {
+ 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);
+
+ return conn_reset_stream(conn, strm, app_error_code);
+}
+
+/*
+ * conn_shutdown_stream_read closes read stream with error code
+ * |app_error_code|. STOP_SENDING frame is scheduled.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
+ return 0;
+ }
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) {
+ return 0;
+ }
+
+ /* 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_STRM_FLAG_RECV_RST))) {
+ ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset -
+ ngtcp2_strm_rx_offset(strm));
+ }
+
+ strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING;
+ strm->app_error_code = app_error_code;
+
+ return conn_stop_sending(conn, strm, app_error_code);
+}
+
+int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ int rv;
+ ngtcp2_strm *strm;
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ rv = conn_shutdown_stream_read(conn, strm, app_error_code);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_shutdown_stream_write(conn, strm, app_error_code);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ ngtcp2_strm *strm;
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ return conn_shutdown_stream_write(conn, strm, app_error_code);
+}
+
+int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ ngtcp2_strm *strm;
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ return conn_shutdown_stream_read(conn, strm, app_error_code);
+}
+
+/*
+ * conn_extend_max_stream_offset extends stream level flow control
+ * window by |datalen| of the stream denoted by |strm|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t datalen) {
+ ngtcp2_strm *top;
+
+ if (datalen > NGTCP2_MAX_VARINT ||
+ strm->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) {
+ strm->rx.unsent_max_offset = NGTCP2_MAX_VARINT;
+ } else {
+ strm->rx.unsent_max_offset += datalen;
+ }
+
+ if (!(strm->flags &
+ (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)) &&
+ !ngtcp2_strm_is_tx_queued(strm) &&
+ conn_should_send_max_stream_data(conn, strm)) {
+ if (!ngtcp2_pq_empty(&conn->tx.strmq)) {
+ top = ngtcp2_conn_tx_strmq_top(conn);
+ strm->cycle = top->cycle;
+ }
+ strm->cycle = conn_tx_strmq_first_cycle(conn);
+ return ngtcp2_conn_tx_strmq_push(conn, strm);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t datalen) {
+ ngtcp2_strm *strm;
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ return conn_extend_max_stream_offset(conn, strm, datalen);
+}
+
+void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, uint64_t datalen) {
+ if (NGTCP2_MAX_VARINT < datalen ||
+ conn->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) {
+ conn->rx.unsent_max_offset = NGTCP2_MAX_VARINT;
+ return;
+ }
+
+ conn->rx.unsent_max_offset += datalen;
+}
+
+void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) {
+ handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n);
+}
+
+void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) {
+ handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n);
+}
+
+const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) {
+ return &conn->dcid.current.cid;
+}
+
+uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) {
+ return conn->version;
+}
+
+int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_rtb *rtb = &conn->pktns.rtb;
+ int rv;
+
+ conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED;
+
+ rv = ngtcp2_rtb_remove_all(rtb, conn, pktns, &conn->cstat);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return rv;
+}
+
+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;
+
+ rtt = ngtcp2_max(rtt, NGTCP2_GRANULARITY);
+
+ cstat->latest_rtt = rtt;
+
+ if (cstat->min_rtt == UINT64_MAX) {
+ 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) {
+ ack_delay =
+ ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay);
+ } else if (ack_delay > 0 && 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;
+ }
+
+ if (rtt > 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)) /
+ 4;
+ 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));
+}
+
+void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) {
+ *cstat = conn->cstat;
+}
+
+static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn,
+ ngtcp2_tstamp *pts,
+ const ngtcp2_tstamp *times) {
+ 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];
+
+ for (i = NGTCP2_PKTNS_ID_HANDSHAKE; i < NGTCP2_PKTNS_ID_MAX; ++i) {
+ if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 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];
+ }
+
+ if (res == NULL && !conn->server) {
+ earliest_ts = UINT64_MAX;
+
+ if (conn->hs_pktns && conn->hs_pktns->crypto.tx.ckm) {
+ res = conn->hs_pktns;
+ } else {
+ res = conn->in_pktns;
+ }
+ }
+
+ if (pts) {
+ *pts = earliest_ts;
+ }
+ return res;
+}
+
+void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_duration timeout;
+ 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);
+
+ if (earliest_loss_time != UINT64_MAX) {
+ cstat->loss_detection_timer = earliest_loss_time;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss_detection_timer=%" PRIu64 " nonzero crypto loss time",
+ cstat->loss_detection_timer);
+ return;
+ }
+
+ if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) &&
+ (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) &&
+ (pktns->rtb.num_retransmittable == 0 ||
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) &&
+ (conn->server ||
+ (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
+ 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;
+ }
+
+ 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 = last_tx_pkt_ts + timeout;
+
+ 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));
+}
+
+int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ int rv;
+ 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;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLOSING:
+ case NGTCP2_CS_DRAINING:
+ cstat->loss_detection_timer = UINT64_MAX;
+ cstat->pto_count = 0;
+ return 0;
+ default:
+ break;
+ }
+
+ if (cstat->loss_detection_timer == UINT64_MAX) {
+ return 0;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer fired");
+
+ if (earliest_loss_time != UINT64_MAX) {
+ rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ return 0;
+ }
+
+ if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+ 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);
+
+ assert(earliest_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;
+ }
+ /* fall through for server so that it can coalesce packets. */
+ /* fall through */
+ case NGTCP2_PKTNS_ID_HANDSHAKE:
+ assert(hs_pktns);
+ hs_pktns->rtb.probe_pkt_left = 1;
+ break;
+ case NGTCP2_PKTNS_ID_APPLICATION:
+ conn->pktns.rtb.probe_pkt_left = 2;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ ++cstat->pto_count;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "pto_count=%zu",
+ cstat->pto_count);
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ return 0;
+}
+
+int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, const size_t datalen) {
+ ngtcp2_pktns *pktns;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_crypto *fr;
+ int rv;
+
+ if (datalen == 0) {
+ return 0;
+ }
+
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ assert(conn->in_pktns);
+ pktns = conn->in_pktns;
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ assert(conn->hs_pktns);
+ pktns = conn->hs_pktns;
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ pktns = &conn->pktns;
+ break;
+ default:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ fr = &frc->fr.crypto;
+
+ fr->type = NGTCP2_FRAME_CRYPTO;
+ fr->offset = pktns->crypto.tx.offset;
+ fr->datacnt = 1;
+ fr->data[0].len = datalen;
+ fr->data[0].base = (uint8_t *)data;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+
+ pktns->crypto.strm.tx.offset += datalen;
+ pktns->crypto.tx.offset += datalen;
+
+ return 0;
+}
+
+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;
+
+ assert(conn->server);
+ assert(token);
+ assert(tokenlen);
+
+ rv = ngtcp2_frame_chain_extralen_new(&nfrc, tokenlen, 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;
+
+ return 0;
+}
+
+ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn) {
+ assert(!ngtcp2_pq_empty(&conn->tx.strmq));
+ return ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe);
+}
+
+void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn) {
+ ngtcp2_strm *strm = ngtcp2_conn_tx_strmq_top(conn);
+ assert(strm);
+ ngtcp2_pq_pop(&conn->tx.strmq);
+ strm->pe.index = NGTCP2_PQ_BAD_INDEX;
+}
+
+int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe);
+}
+
+size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) {
+ return ngtcp2_ksl_len(&conn->scid.set);
+}
+
+size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) {
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+
+ for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ scid = ngtcp2_ksl_it_get(&it);
+ *dest++ = scid->cid;
+ }
+
+ return ngtcp2_ksl_len(&conn->scid.set);
+}
+
+size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) {
+ size_t n = 1; /* for conn->dcid.current */
+ ngtcp2_pv *pv = conn->pv;
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
+ return 0;
+ }
+
+ if (pv) {
+ if (pv->dcid.seq != conn->dcid.current.seq) {
+ ++n;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq != pv->dcid.seq) {
+ ++n;
+ }
+ }
+
+ n += ngtcp2_ringbuf_len(&conn->dcid.retired);
+
+ return n;
+}
+
+static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest,
+ const ngtcp2_dcid *src) {
+ 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);
+}
+
+size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_cid_token *orig = dest;
+ ngtcp2_dcid *dcid;
+ size_t len, i;
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
+ return 0;
+ }
+
+ copy_dcid_to_cid_token(dest, &conn->dcid.current);
+ ++dest;
+
+ if (pv) {
+ if (pv->dcid.seq != conn->dcid.current.seq) {
+ copy_dcid_to_cid_token(dest, &pv->dcid);
+ ++dest;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq != pv->dcid.seq) {
+ copy_dcid_to_cid_token(dest, &pv->fallback_dcid);
+ ++dest;
+ }
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+ copy_dcid_to_cid_token(dest, dcid);
+ ++dest;
+ }
+
+ return (size_t)(dest - orig);
+}
+
+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));
+ 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);
+}
+
+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;
+
+ assert(!conn->server);
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ if (conn->remote.transport_params.disable_active_migration ||
+ conn->dcid.current.cid.datalen == 0 ||
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+ return NGTCP2_ERR_CONN_ID_BLOCKED;
+ }
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+
+ rv = conn_stop_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_dcid_copy(&conn->dcid.current, dcid);
+ ngtcp2_path_copy(&conn->dcid.current.ps.path, path);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_reset_congestion_state(conn);
+ conn_reset_ecn_validation_state(conn);
+
+ return 0;
+}
+
+uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) {
+ return conn->local.uni.max_streams;
+}
+
+uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) {
+ return conn->tx.max_offset - conn->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);
+
+ return n > conn->local.bidi.max_streams
+ ? 0
+ : conn->local.bidi.max_streams - n + 1;
+}
+
+uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) {
+ uint64_t n = ngtcp2_ord_stream_id(conn->local.uni.next_stream_id);
+
+ return n > conn->local.uni.max_streams ? 0
+ : conn->local.uni.max_streams - n + 1;
+}
+
+ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) {
+ ngtcp2_duration trpto;
+ ngtcp2_duration idle_timeout;
+
+ /* 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 ||
+ (conn->local.transport_params.max_idle_timeout &&
+ conn->local.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;
+ }
+
+ 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);
+
+ 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);
+}
+
+void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx) {
+ assert(conn->in_pktns);
+ conn->in_pktns->crypto.ctx = *ctx;
+}
+
+const ngtcp2_crypto_ctx *ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn) {
+ assert(conn->in_pktns);
+ return &conn->in_pktns->crypto.ctx;
+}
+
+void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx) {
+ assert(!conn->crypto.retry_aead_ctx.native_handle);
+
+ conn->crypto.retry_aead = *aead;
+ conn->crypto.retry_aead_ctx = *aead_ctx;
+}
+
+void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx) {
+ assert(conn->hs_pktns);
+ conn->hs_pktns->crypto.ctx = *ctx;
+ conn->pktns.crypto.ctx = *ctx;
+}
+
+const ngtcp2_crypto_ctx *ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn) {
+ return &conn->pktns.crypto.ctx;
+}
+
+void ngtcp2_conn_set_early_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx) {
+ conn->early.ctx = *ctx;
+}
+
+const ngtcp2_crypto_ctx *ngtcp2_conn_get_early_crypto_ctx(ngtcp2_conn *conn) {
+ return &conn->early.ctx;
+}
+
+void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn) {
+ return conn->crypto.tls_native_handle;
+}
+
+void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn,
+ void *tls_native_handle) {
+ 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_set_tls_error(ngtcp2_conn *conn, int liberr) {
+ conn->crypto.tls_error = liberr;
+}
+
+int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) {
+ return conn->crypto.tls_error;
+}
+
+int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) {
+ return conn_local_stream(conn, stream_id);
+}
+
+int ngtcp2_conn_is_server(ngtcp2_conn *conn) { return conn->server; }
+
+int ngtcp2_conn_after_retry(ngtcp2_conn *conn) {
+ return (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) != 0;
+}
+
+int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id,
+ void *stream_user_data) {
+ ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ strm->stream_user_data = stream_user_data;
+
+ return 0;
+}
+
+void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
+ const ngtcp2_path *path,
+ const uint8_t *data) {
+ ngtcp2_path_storage_init2(&pcent->ps, path);
+ memcpy(pcent->data, data, sizeof(pcent->data));
+}
+
+void ngtcp2_settings_default(ngtcp2_settings *settings) {
+ memset(settings, 0, sizeof(*settings));
+ settings->cc_algo = NGTCP2_CC_ALGO_CUBIC;
+ settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
+ settings->ack_thresh = 2;
+}
+
+void ngtcp2_transport_params_default(ngtcp2_transport_params *params) {
+ memset(params, 0, sizeof(*params));
+ params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_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 =
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+}
+
+/* The functions prefixed with ngtcp2_pkt_ are usually put inside
+ ngtcp2_pkt.c. This function uses encryption construct and uses
+ test data defined only in ngtcp2_conn_test.c, so it is written
+ 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_crypto_cipher_ctx *hp_ctx) {
+ ngtcp2_pkt_hd hd;
+ ngtcp2_crypto_km ckm;
+ ngtcp2_crypto_cc cc;
+ ngtcp2_ppe ppe;
+ ngtcp2_frame fr = {0};
+ int rv;
+
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid,
+ scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version,
+ /* len = */ 0);
+
+ ngtcp2_vec_init(&ckm.secret, NULL, 0);
+ ngtcp2_vec_init(&ckm.iv, iv, 12);
+ ckm.aead_ctx = *aead_ctx;
+ ckm.pkt_num = 0;
+ ckm.flags = NGTCP2_CRYPTO_KM_FLAG_NONE;
+
+ cc.aead = *aead;
+ cc.hp = *hp;
+ cc.ckm = &ckm;
+ cc.hp_ctx = *hp_ctx;
+ cc.encrypt = encrypt;
+ cc.hp_mask = hp_mask;
+
+ ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
+
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return rv;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
+ fr.connection_close.error_code = error_code;
+
+ rv = ngtcp2_ppe_encode_frame(&ppe, &fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return rv;
+ }
+
+ return ngtcp2_ppe_final(&ppe, NULL);
+}
+
+int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); }
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h
new file mode 100644
index 0000000000..825e4502e1
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h
@@ -0,0 +1,822 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CONN_H
+#define NGTCP2_CONN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_crypto.h"
+#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_pv.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_buf.h"
+#include "ngtcp2_ppe.h"
+#include "ngtcp2_qlog.h"
+#include "ngtcp2_rst.h"
+
+typedef enum {
+ /* Client specific handshake states */
+ NGTCP2_CS_CLIENT_INITIAL,
+ NGTCP2_CS_CLIENT_WAIT_HANDSHAKE,
+ NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED,
+ /* Server specific handshake states */
+ NGTCP2_CS_SERVER_INITIAL,
+ NGTCP2_CS_SERVER_WAIT_HANDSHAKE,
+ NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED,
+ /* Shared by both client and server */
+ NGTCP2_CS_POST_HANDSHAKE,
+ NGTCP2_CS_CLOSING,
+ NGTCP2_CS_DRAINING,
+} ngtcp2_conn_state;
+
+/* NGTCP2_MAX_STREAMS is the maximum number of streams. */
+#define NGTCP2_MAX_STREAMS (1LL << 60)
+
+/* NGTCP2_MAX_NUM_BUFFED_RX_PKTS is the maximum number of buffered
+ reordered packets. */
+#define NGTCP2_MAX_NUM_BUFFED_RX_PKTS 4
+
+/* NGTCP2_MAX_REORDERED_CRYPTO_DATA is the maximum offset of crypto
+ data which is not continuous. In other words, there is a gap of
+ unreceived data. */
+#define NGTCP2_MAX_REORDERED_CRYPTO_DATA 65536
+
+/* NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA is the maximum offset of received
+ crypto stream in Initial packet. We set this hard limit here
+ because crypto stream is unbounded. */
+#define NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA 65536
+/* NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA is the maximum offset of
+ received crypto stream in Handshake packet. We set this hard limit
+ here because crypto stream is unbounded. */
+#define NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA 65536
+
+/* NGTCP2_MAX_RETRIES is the number of Retry packet which client can
+ accept. */
+#define NGTCP2_MAX_RETRIES 3
+
+/* NGTCP2_MAX_BOUND_DCID_POOL_SIZE is the maximum number of
+ destination connection ID which have been bound to a particular
+ path, but not yet used as primary path and path validation is not
+ performed from the local endpoint. */
+#define NGTCP2_MAX_BOUND_DCID_POOL_SIZE 4
+/* NGTCP2_MAX_DCID_POOL_SIZE is the maximum number of destination
+ connection ID the remote endpoint provides to store. It must be
+ the power of 2. */
+#define NGTCP2_MAX_DCID_POOL_SIZE 8
+/* NGTCP2_MAX_DCID_RETIRED_SIZE is the maximum number of retired DCID
+ kept to catch in-flight packet on retired path. */
+#define NGTCP2_MAX_DCID_RETIRED_SIZE 2
+/* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source
+ connection ID the local endpoint provides to the remote endpoint.
+ The chosen value was described in old draft. Now a remote endpoint
+ tells the maximum value. The value can be quite large, and we have
+ to put the sane limit.*/
+#define NGTCP2_MAX_SCID_POOL_SIZE 8
+
+/* NGTCP2_MAX_NON_ACK_TX_PKT is the maximum number of continuous non
+ ACK-eliciting packets. */
+#define NGTCP2_MAX_NON_ACK_TX_PKT 3
+
+/* NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS is the maximum number of ECN marked
+ packets sent in NGTCP2_ECN_STATE_TESTING period. */
+#define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10
+
+/*
+ * ngtcp2_max_frame is defined so that it covers the largest ACK
+ * frame.
+ */
+typedef union ngtcp2_max_frame {
+ ngtcp2_frame fr;
+ struct {
+ ngtcp2_ack ack;
+ /* ack includes 1 ngtcp2_ack_blk. */
+ ngtcp2_ack_blk blks[NGTCP2_MAX_ACK_BLKS - 1];
+ } ackfr;
+} ngtcp2_max_frame;
+
+typedef struct ngtcp2_path_challenge_entry {
+ ngtcp2_path_storage ps;
+ uint8_t data[8];
+} ngtcp2_path_challenge_entry;
+
+void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
+ const ngtcp2_path *path,
+ 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
+/* 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
+/* 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
+/* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry
+ packet. */
+#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10
+/* 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
+/* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint
+ confirmed completion of handshake. */
+#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80
+/* 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
+/* 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
+/* 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
+/* 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
+/* 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
+/* 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
+
+typedef struct ngtcp2_crypto_data {
+ ngtcp2_buf buf;
+ /* pkt_type is the type of packet to send data in buf. If it is 0,
+ it must be sent in Short packet. Otherwise, it is sent the long
+ packet type denoted by pkt_type. */
+ uint8_t pkt_type;
+} ngtcp2_crypto_data;
+
+typedef struct ngtcp2_pktns {
+ struct {
+ /* last_pkt_num is the packet number which the local endpoint sent
+ last time.*/
+ int64_t last_pkt_num;
+ ngtcp2_frame_chain *frq;
+ /* num_non_ack_pkt is the number of continuous non ACK-eliciting
+ packets. */
+ size_t num_non_ack_pkt;
+
+ struct {
+ /* ect0 is the number of QUIC packets, not UDP datagram, which
+ are sent in UDP datagram with ECT0 marking. */
+ size_t ect0;
+ /* start_pkt_num is the lowest packet number that are sent
+ during ECN validation period. */
+ int64_t start_pkt_num;
+ /* validation_pkt_sent is the number of QUIC packets sent during
+ validation period. */
+ size_t validation_pkt_sent;
+ /* validation_pkt_lost is the number of QUIC packets lost during
+ validation period. */
+ size_t validation_pkt_lost;
+ } ecn;
+ } tx;
+
+ struct {
+ /* pngap tracks received packet number in order to suppress
+ duplicated packet number. */
+ ngtcp2_gaptr pngap;
+ /* max_pkt_num is the largest packet number received so far. */
+ int64_t max_pkt_num;
+ /* max_pkt_ts is the timestamp when max_pkt_num packet is
+ received. */
+ ngtcp2_tstamp max_pkt_ts;
+ /* max_ack_eliciting_pkt_num is the largest ack-eliciting packet
+ number received so far. */
+ int64_t max_ack_eliciting_pkt_num;
+ /*
+ * buffed_pkts is buffered packets which cannot be decrypted with
+ * the current encryption level.
+ *
+ * In server Initial encryption level, 0-RTT packet may be buffered.
+ * In server Handshake encryption level, Short packet may be buffered.
+ *
+ * In client Initial encryption level, Handshake or Short packet may
+ * be buffered. In client Handshake encryption level, Short packet
+ * may be buffered.
+ *
+ * - 0-RTT packet is only buffered in server Initial encryption
+ * level ngtcp2_pktns.
+ *
+ * - Handshake packet is only buffered in client Handshake
+ * encryption level ngtcp2_pktns.
+ *
+ * - Short packet is only buffered in Short encryption level
+ * ngtcp2_pktns.
+ */
+ ngtcp2_pkt_chain *buffed_pkts;
+
+ struct {
+ /* ect0, ect1, and ce are the number of QUIC packets received
+ with those markings. */
+ size_t ect0;
+ size_t ect1;
+ size_t ce;
+ struct {
+ /* ect0, ect1, ce are the ECN counts received in the latest
+ ACK frame. */
+ size_t ect0;
+ size_t ect1;
+ size_t ce;
+ } ack;
+ } ecn;
+ } rx;
+
+ struct {
+ struct {
+ /* frq contains crypto data sorted by their offset. */
+ ngtcp2_ksl frq;
+ /* offset is the offset of crypto stream in this packet number
+ space. */
+ uint64_t offset;
+ /* ckm is a cryptographic key, and iv to encrypt outgoing
+ packets. */
+ ngtcp2_crypto_km *ckm;
+ /* hp_ctx is cipher context for packet header protection. */
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ } tx;
+
+ struct {
+ /* ckm is a cryptographic key, and iv to decrypt incoming
+ packets. */
+ ngtcp2_crypto_km *ckm;
+ /* hp_ctx is cipher context for packet header protection. */
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ } rx;
+
+ ngtcp2_strm strm;
+ ngtcp2_crypto_ctx ctx;
+ } crypto;
+
+ ngtcp2_acktr acktr;
+ ngtcp2_rtb rtb;
+} ngtcp2_pktns;
+
+typedef enum ngtcp2_ecn_state {
+ NGTCP2_ECN_STATE_TESTING,
+ NGTCP2_ECN_STATE_UNKNOWN,
+ NGTCP2_ECN_STATE_FAILED,
+ NGTCP2_ECN_STATE_CAPABLE,
+} ngtcp2_ecn_state;
+
+struct ngtcp2_conn {
+ 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. */
+ ngtcp2_cid rcid;
+ /* oscid is the source connection ID initially used by the local
+ endpoint. */
+ ngtcp2_cid oscid;
+ /* retry_scid is the source connection ID from Retry packet. Client
+ records it in order to verify retry_source_connection_id
+ transport parameter. Server does not use this field. */
+ ngtcp2_cid retry_scid;
+ ngtcp2_pktns *in_pktns;
+ ngtcp2_pktns *hs_pktns;
+ ngtcp2_pktns pktns;
+
+ struct {
+ /* current is the current destination connection ID. */
+ 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;
+ /* unused is a set of unused CID received from peer. */
+ ngtcp2_ringbuf 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;
+ /* 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;
+ /* zerolen_seq is a pseudo sequence number of zero-length
+ Destination Connection ID in order to distinguish between
+ them. */
+ uint64_t zerolen_seq;
+ } dcid;
+
+ struct {
+ /* set is a set of CID sent to peer. The peer can use any CIDs in
+ this set. This includes used CID as well as unused ones. */
+ ngtcp2_ksl set;
+ /* used is a set of CID used by peer. The sort function of this
+ priority queue takes timestamp when CID is retired and sorts
+ them in ascending order. */
+ ngtcp2_pq used;
+ /* last_seq is the last sequence number of connection ID. */
+ uint64_t last_seq;
+ /* num_retired is the number of retired Connection ID still
+ included in set. */
+ size_t num_retired;
+ } scid;
+
+ struct {
+ /* strmq contains ngtcp2_strm which has frames to send. */
+ ngtcp2_pq strmq;
+ /* ack is ACK frame. The underlying buffer is reused. */
+ ngtcp2_frame *ack;
+ /* max_ack_blks is the number of additional ngtcp2_ack_blk which
+ ack can contain. */
+ size_t max_ack_blks;
+ /* offset is the offset the local endpoint has sent to the remote
+ endpoint. */
+ uint64_t offset;
+ /* max_offset is the maximum offset that local endpoint can
+ send. */
+ uint64_t max_offset;
+ /* last_max_data_ts is the timestamp when last MAX_DATA frame is
+ sent. */
+ ngtcp2_tstamp last_max_data_ts;
+
+ struct {
+ /* state is the state of ECN validation */
+ ngtcp2_ecn_state state;
+ /* validation_start_ts is the timestamp when ECN validation is
+ started. It is UINT64_MAX if it has not started yet. */
+ ngtcp2_tstamp validation_start_ts;
+ /* dgram_sent is the number of UDP datagram sent during ECN
+ validation period. */
+ size_t dgram_sent;
+ } ecn;
+ } tx;
+
+ struct {
+ /* unsent_max_offset is the maximum offset that remote endpoint
+ can send without extending MAX_DATA. This limit is not yet
+ notified to the remote endpoint. */
+ uint64_t unsent_max_offset;
+ /* offset is the cumulative sum of stream data received for this
+ connection. */
+ uint64_t offset;
+ /* max_offset is the maximum offset that remote endpoint can
+ send. */
+ uint64_t max_offset;
+ /* 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;
+ } rx;
+
+ struct {
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ ngtcp2_crypto_ctx ctx;
+ /* discard_started_ts is the timestamp when the timer to discard
+ early key has started. Used by server only. */
+ ngtcp2_tstamp discard_started_ts;
+ } early;
+
+ struct {
+ ngtcp2_settings settings;
+ /* transport_params is the local transport parameters. It is used
+ for Short packet only. */
+ ngtcp2_transport_params transport_params;
+ struct {
+ /* max_streams is the maximum number of bidirectional streams which
+ the local endpoint can open. */
+ uint64_t max_streams;
+ /* next_stream_id is the bidirectional stream ID which the local
+ endpoint opens next. */
+ int64_t next_stream_id;
+ } bidi;
+
+ struct {
+ /* max_streams is the maximum number of unidirectional streams
+ which the local endpoint can open. */
+ uint64_t max_streams;
+ /* next_stream_id is the unidirectional stream ID which the
+ local endpoint opens next. */
+ int64_t next_stream_id;
+ } uni;
+ } local;
+
+ struct {
+ /* transport_params is the received transport parameters during
+ handshake. It is used for Short packet only. */
+ 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;
+ struct {
+ ngtcp2_idtr idtr;
+ /* unsent_max_streams is the maximum number of streams of peer
+ initiated bidirectional stream which the local endpoint can
+ accept. This limit is not yet notified to the remote
+ endpoint. */
+ uint64_t unsent_max_streams;
+ /* max_streams is the maximum number of streams of peer
+ initiated bidirectional stream which the local endpoint can
+ accept. */
+ uint64_t max_streams;
+ } bidi;
+
+ struct {
+ ngtcp2_idtr idtr;
+ /* unsent_max_streams is the maximum number of streams of peer
+ initiated unidirectional stream which the local endpoint can
+ accept. This limit is not yet notified to the remote
+ endpoint. */
+ uint64_t unsent_max_streams;
+ /* max_streams is the maximum number of streams of peer
+ initiated unidirectional stream which the local endpoint can
+ accept. */
+ uint64_t max_streams;
+ } uni;
+ } remote;
+
+ struct {
+ struct {
+ /* new_tx_ckm is a new sender 1RTT key which has not been
+ used. */
+ ngtcp2_crypto_km *new_tx_ckm;
+ /* new_rx_ckm is a new receiver 1RTT key which has not
+ successfully decrypted incoming packet yet. */
+ ngtcp2_crypto_km *new_rx_ckm;
+ /* old_rx_ckm is an old receiver 1RTT key. */
+ ngtcp2_crypto_km *old_rx_ckm;
+ /* confirmed_ts is the time instant when the key update is
+ confirmed by the local endpoint last time. UINT64_MAX means
+ undefined value. */
+ ngtcp2_tstamp confirmed_ts;
+ } key_update;
+
+ /* tls_native_handle is a native handle to TLS session object. */
+ void *tls_native_handle;
+ /* decrypt_hp_buf is a buffer which is used to write unprotected
+ packet header. */
+ ngtcp2_vec decrypt_hp_buf;
+ /* decrypt_buf is a buffer which is used to write decrypted data. */
+ ngtcp2_vec decrypt_buf;
+ /* retry_aead is AEAD to verify Retry packet integrity. It is
+ used by client only. */
+ ngtcp2_crypto_aead retry_aead;
+ /* retry_aead_ctx is AEAD cipher context to verify Retry packet
+ integrity. It is used by client only. */
+ ngtcp2_crypto_aead_ctx retry_aead_ctx;
+ /* tls_error is TLS related error. */
+ int tls_error;
+ /* decryption_failure_count is the number of received packets that
+ fail authentication. */
+ uint64_t decryption_failure_count;
+ } crypto;
+
+ /* pkt contains the packet intermediate construction data to support
+ NGTCP2_WRITE_STREAM_FLAG_MORE */
+ struct {
+ ngtcp2_crypto_cc cc;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_ppe ppe;
+ ngtcp2_frame_chain **pfrc;
+ int pkt_empty;
+ int hd_logged;
+ uint8_t rtb_entry_flags;
+ int was_client_initial;
+ ngtcp2_ssize hs_spktlen;
+ } pkt;
+
+ ngtcp2_map strms;
+ ngtcp2_conn_stat cstat;
+ ngtcp2_pv *pv;
+ ngtcp2_log log;
+ ngtcp2_qlog qlog;
+ ngtcp2_rst rst;
+ ngtcp2_cc_algo cc_algo;
+ ngtcp2_cc cc;
+ const ngtcp2_mem *mem;
+ /* idle_ts is the time instant when idle timer started. */
+ ngtcp2_tstamp idle_ts;
+ void *user_data;
+ uint32_t version;
+ /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */
+ uint16_t flags;
+ int server;
+};
+
+typedef enum ngtcp2_vmsg_type {
+ NGTCP2_VMSG_TYPE_STREAM,
+ NGTCP2_VMSG_TYPE_DATAGRAM,
+} ngtcp2_vmsg_type;
+
+typedef struct ngtcp2_vmsg_stream {
+ /* strm is a stream that data is sent to. */
+ ngtcp2_strm *strm;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_WRITE_STREAM_FLAG_*. */
+ uint32_t flags;
+ /* data is the pointer to ngtcp2_vec array which contains the stream
+ data to send. */
+ const ngtcp2_vec *data;
+ /* datacnt is the number of ngtcp2_vec pointed by data. */
+ size_t datacnt;
+ /* pdatalen is the pointer to the variable which the number of bytes
+ written is assigned to if pdatalen is not NULL. */
+ ngtcp2_ssize *pdatalen;
+} ngtcp2_vmsg_stream;
+
+typedef struct ngtcp2_vmsg_datagram {
+ /* data is the pointer to ngtcp2_vec array which contains the data
+ to send. */
+ const ngtcp2_vec *data;
+ /* datacnt is the number of ngtcp2_vec pointed by data. */
+ size_t datacnt;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_WRITE_DATAGRAM_FLAG_*. */
+ uint32_t flags;
+ /* paccepted is the pointer to the variable which, if it is not
+ NULL, is assigned nonzero if data is written to a packet. */
+ int *paccepted;
+} ngtcp2_vmsg_datagram;
+
+typedef struct ngtcp2_vmsg {
+ ngtcp2_vmsg_type type;
+ union {
+ ngtcp2_vmsg_stream stream;
+ ngtcp2_vmsg_datagram datagram;
+ };
+} ngtcp2_vmsg;
+
+/*
+ * ngtcp2_conn_sched_ack stores packet number |pkt_num| and its
+ * reception timestamp |ts| in order to send its ACK.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_PROTO
+ * Same packet number has already been added.
+ */
+int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr,
+ int64_t pkt_num, int active_ack, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_find_stream returns a stream whose stream ID is
+ * |stream_id|. If no such stream is found, it returns NULL.
+ */
+ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id);
+
+/*
+ * conn_init_stream initializes |strm|. Its stream ID is |stream_id|.
+ * This function adds |strm| to conn->strms. |strm| must be allocated
+ * by the caller.
+ *
+ * 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-callback function failed.
+ */
+int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ int64_t stream_id, void *stream_user_data);
+
+/*
+ * ngtcp2_conn_close_stream closes stream |strm|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Stream is not found.
+ * 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);
+
+/*
+ * ngtcp2_conn_close_stream closes stream |strm| if no further
+ * transmission and reception are allowed, and all reordered incoming
+ * data are emitted to the application, and the transmitted data are
+ * acked.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Stream is not found.
+ * 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);
+
+/*
+ * ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest
+ * RTT which is not adjusted by ack delay. |ack_delay| is unscaled
+ * 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|.
+ */
+void 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);
+
+/*
+ * ngtcp2_conn_detect_lost_pkt detects lost packets.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_tx_strmq_top returns the ngtcp2_strm which sits on the
+ * top of queue. tx_strmq must not be empty.
+ */
+ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_tx_strmq_pop pops the ngtcp2_strm from the queue.
+ * tx_strmq must not be empty.
+ */
+void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_tx_strmq_push pushes |strm| into tx_strmq.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_conn_internal_expiry returns the minimum expiry time among
+ * all timers in |conn|.
+ */
+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);
+
+/*
+ * 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.
+ *
+ * The packet written by this function will not be retransmitted.
+ *
+ * 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-defined callback function failed.
+ */
+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);
+
+/*
+ * ngtcp2_conn_commit_local_transport_params commits the local
+ * transport parameters, which is currently set to
+ * conn->local.settings.transport_params. This function will do some
+ * amends on transport parameters for adjusting default values.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * CID in preferred address equals to the original SCID.
+ */
+int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_lost_pkt_expiry returns the earliest expiry time of
+ * lost packet.
+ */
+ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_remove_lost_pkt removes the expired lost packet.
+ */
+void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_resched_frames reschedules frames linked from |*pfrc|
+ * for retransmission.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_frame_chain **pfrc);
+
+uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of
+ * delayed protected ACK. One should call
+ * `ngtcp2_conn_cancel_expired_ack_delay_timer` and
+ * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_writev_stream`) when it
+ * expires. It returns UINT64_MAX if there is no expiry.
+ */
+ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_cancel_expired_ack_delay_timer` stops expired ACK
+ * delay timer. |ts| is the current time. This function must be
+ * called when `ngtcp2_conn_ack_delay_expiry` <= ts.
+ */
+void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_loss_detection_expiry` returns the expiry time point
+ * of loss detection timer. One should call
+ * `ngtcp2_conn_on_loss_detection_timer` and `ngtcp2_conn_write_pkt`
+ * (or `ngtcp2_conn_writev_stream`) when it expires. It returns
+ * UINT64_MAX if loss detection timer is not armed.
+ */
+ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn);
+
+#endif /* NGTCP2_CONN_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c
new file mode 100644
index 0000000000..9064218dac
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c
@@ -0,0 +1,257 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_conv.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_pkt.h"
+
+uint64_t ngtcp2_get_uint64(const uint8_t *p) {
+ uint64_t n;
+ memcpy(&n, p, 8);
+ return ngtcp2_ntohl64(n);
+}
+
+uint64_t ngtcp2_get_uint48(const uint8_t *p) {
+ uint64_t n = 0;
+ memcpy(((uint8_t *)&n) + 2, p, 6);
+ return ngtcp2_ntohl64(n);
+}
+
+uint32_t ngtcp2_get_uint32(const uint8_t *p) {
+ uint32_t n;
+ memcpy(&n, p, 4);
+ return ngtcp2_ntohl(n);
+}
+
+uint32_t ngtcp2_get_uint24(const uint8_t *p) {
+ uint32_t n = 0;
+ memcpy(((uint8_t *)&n) + 1, p, 3);
+ return ngtcp2_ntohl(n);
+}
+
+uint16_t ngtcp2_get_uint16(const uint8_t *p) {
+ uint16_t n;
+ memcpy(&n, p, 2);
+ return ngtcp2_ntohs(n);
+}
+
+uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p) {
+ union {
+ char b[8];
+ uint16_t n16;
+ uint32_t n32;
+ uint64_t n64;
+ } n;
+
+ *plen = (size_t)(1u << (*p >> 6));
+
+ switch (*plen) {
+ case 1:
+ return *p;
+ case 2:
+ memcpy(&n, p, 2);
+ n.b[0] &= 0x3f;
+ return ngtcp2_ntohs(n.n16);
+ case 4:
+ memcpy(&n, p, 4);
+ n.b[0] &= 0x3f;
+ return ngtcp2_ntohl(n.n32);
+ case 8:
+ memcpy(&n, p, 8);
+ n.b[0] &= 0x3f;
+ return ngtcp2_ntohl64(n.n64);
+ default:
+ assert(0);
+ }
+
+ return 0;
+}
+
+int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen) {
+ switch (pkt_numlen) {
+ case 1:
+ return *p;
+ case 2:
+ return (int64_t)ngtcp2_get_uint16(p);
+ case 3:
+ return (int64_t)ngtcp2_get_uint24(p);
+ case 4:
+ return (int64_t)ngtcp2_get_uint32(p);
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n) {
+ n = ngtcp2_htonl64(n);
+ return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n) {
+ n = ngtcp2_htonl64(n);
+ return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 2, 6);
+}
+
+uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n) {
+ n = ngtcp2_htonl(n);
+ return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n) {
+ n = ngtcp2_htonl(n);
+ return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 1, 3);
+}
+
+uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n) {
+ n = ngtcp2_htons(n);
+ return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) {
+ uint8_t *rv;
+ if (n < 64) {
+ *p++ = (uint8_t)n;
+ return p;
+ }
+ if (n < 16384) {
+ rv = ngtcp2_put_uint16be(p, (uint16_t)n);
+ *p |= 0x40;
+ return rv;
+ }
+ if (n < 1073741824) {
+ rv = ngtcp2_put_uint32be(p, (uint32_t)n);
+ *p |= 0x80;
+ return rv;
+ }
+ assert(n < 4611686018427387904ULL);
+ rv = ngtcp2_put_uint64be(p, n);
+ *p |= 0xc0;
+ return rv;
+}
+
+uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) {
+ uint8_t *rv;
+
+ assert(n < 16384);
+
+ rv = ngtcp2_put_uint16be(p, n);
+ *p |= 0x40;
+
+ return rv;
+}
+
+uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len) {
+ switch (len) {
+ case 1:
+ *p++ = (uint8_t)pkt_num;
+ return p;
+ case 2:
+ ngtcp2_put_uint16be(p, (uint16_t)pkt_num);
+ return p + 2;
+ case 3:
+ ngtcp2_put_uint24be(p, (uint32_t)pkt_num);
+ return p + 3;
+ case 4:
+ ngtcp2_put_uint32be(p, (uint32_t)pkt_num);
+ return p + 4;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+size_t ngtcp2_get_varint_len(const uint8_t *p) {
+ return (size_t)(1u << (*p >> 6));
+}
+
+size_t ngtcp2_put_varint_len(uint64_t n) {
+ if (n < 64) {
+ return 1;
+ }
+ if (n < 16384) {
+ return 2;
+ }
+ if (n < 1073741824) {
+ return 4;
+ }
+ assert(n < 4611686018427387904ULL);
+ return 8;
+}
+
+int64_t ngtcp2_nth_server_bidi_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_SERVER_STREAM_ID_BIDI;
+ }
+
+ return (int64_t)(((n - 1) << 2) | 0x01);
+}
+
+int64_t ngtcp2_nth_client_bidi_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_CLIENT_STREAM_ID_BIDI;
+ }
+
+ return (int64_t)((n - 1) << 2);
+}
+
+int64_t ngtcp2_nth_server_uni_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_SERVER_STREAM_ID_UNI;
+ }
+
+ return (int64_t)(((n - 1) << 2) | 0x03);
+}
+
+int64_t ngtcp2_nth_client_uni_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_CLIENT_STREAM_ID_UNI;
+ }
+
+ return (int64_t)(((n - 1) << 2) | 0x02);
+}
+
+uint64_t ngtcp2_ord_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2) + 1;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h
new file mode 100644
index 0000000000..dcff0fdb8c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h
@@ -0,0 +1,285 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CONV_H
+#define NGTCP2_CONV_H
+
+#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++ = 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
+ * order.
+ */
+uint64_t ngtcp2_get_uint64(const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint48 reads 6 bytes from |p| as 48 bits unsigned
+ * integer encoded as network byte order, and returns it in host byte
+ * order.
+ */
+uint64_t ngtcp2_get_uint48(const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint32 reads 4 bytes from |p| as 32 bits unsigned
+ * integer encoded as network byte order, and returns it in host byte
+ * order.
+ */
+uint32_t ngtcp2_get_uint32(const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint24 reads 3 bytes from |p| as 24 bits unsigned
+ * integer encoded as network byte order, and returns it in host byte
+ * order.
+ */
+uint32_t ngtcp2_get_uint24(const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint16 reads 2 bytes from |p| as 16 bits unsigned
+ * integer encoded as network byte order, and returns it in host byte
+ * order.
+ */
+uint16_t ngtcp2_get_uint16(const uint8_t *p);
+
+/*
+ * ngtcp2_get_varint reads variable-length integer from |p|, and
+ * returns it in host byte order. The number of bytes read is stored
+ * in |*plen|.
+ */
+uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p);
+
+/*
+ * ngtcp2_get_pkt_num reads encoded packet number from |p|. The
+ * packet number is encoed in |pkt_numlen| bytes.
+ */
+int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen);
+
+/*
+ * ngtcp2_put_uint64be writes |n| in host byte order in |p| in network
+ * byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n);
+
+/*
+ * ngtcp2_put_uint48be writes |n| in host byte order in |p| in network
+ * byte order. It writes only least significant 48 bits. It returns
+ * the one beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n);
+
+/*
+ * ngtcp2_put_uint32be writes |n| in host byte order in |p| in network
+ * byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n);
+
+/*
+ * ngtcp2_put_uint24be writes |n| in host byte order in |p| in network
+ * byte order. It writes only least significant 24 bits. It returns
+ * the one beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n);
+
+/*
+ * ngtcp2_put_uint16be writes |n| in host byte order in |p| in network
+ * byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n);
+
+/*
+ * ngtcp2_put_varint writes |n| in |p| using variable-length integer
+ * encoding. It returns the one beyond of the last written position.
+ */
+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
+ * last written position.
+ */
+uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n);
+
+/*
+ * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It
+ * returns the one beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len);
+
+/*
+ * ngtcp2_get_varint_len returns the required number of bytes to read
+ * variable-length integer starting at |p|.
+ */
+size_t ngtcp2_get_varint_len(const uint8_t *p);
+
+/*
+ * ngtcp2_put_varint_len returns the required number of bytes to
+ * encode |n|.
+ */
+size_t ngtcp2_put_varint_len(uint64_t n);
+
+/*
+ * ngtcp2_nth_server_bidi_id returns |n|-th server bidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_SERVER_STREAM_ID_BIDI, this function returns
+ * NGTCP2_MAX_SERVER_STREAM_ID_BIDI.
+ */
+int64_t ngtcp2_nth_server_bidi_id(uint64_t n);
+
+/*
+ * ngtcp2_nth_client_bidi_id returns |n|-th client bidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_CLIENT_STREAM_ID_BIDI, this function returns
+ * NGTCP2_MAX_CLIENT_STREAM_ID_BIDI.
+ */
+int64_t ngtcp2_nth_client_bidi_id(uint64_t n);
+
+/*
+ * ngtcp2_nth_server_uni_id returns |n|-th server unidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_SERVER_STREAM_ID_UNI, this function returns
+ * NGTCP2_MAX_SERVER_STREAM_ID_UNI.
+ */
+int64_t ngtcp2_nth_server_uni_id(uint64_t n);
+
+/*
+ * ngtcp2_nth_client_uni_id returns |n|-th client unidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_CLIENT_STREAM_ID_UNI, this function returns
+ * NGTCP2_MAX_CLIENT_STREAM_ID_UNI.
+ */
+int64_t ngtcp2_nth_client_uni_id(uint64_t n);
+
+/*
+ * ngtcp2_ord_stream_id returns the ordinal number of |stream_id|.
+ */
+uint64_t ngtcp2_ord_stream_id(int64_t stream_id);
+
+#endif /* NGTCP2_CONV_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c
new file mode 100644
index 0000000000..e11287fd4a
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c
@@ -0,0 +1,749 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_crypto.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_conn.h"
+
+int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen,
+ const ngtcp2_mem *mem) {
+ int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, ivlen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (secretlen) {
+ memcpy((*pckm)->secret.base, secret, secretlen);
+ }
+ if (aead_ctx) {
+ (*pckm)->aead_ctx = *aead_ctx;
+ }
+ memcpy((*pckm)->iv.base, iv, ivlen);
+
+ return 0;
+}
+
+int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen,
+ size_t ivlen, const ngtcp2_mem *mem) {
+ size_t len;
+ uint8_t *p;
+
+ len = sizeof(ngtcp2_crypto_km) + secretlen + ivlen;
+
+ *pckm = ngtcp2_mem_malloc(mem, len);
+ if (*pckm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km);
+ (*pckm)->secret.base = p;
+ (*pckm)->secret.len = secretlen;
+ p += secretlen;
+ (*pckm)->iv.base = p;
+ (*pckm)->iv.len = ivlen;
+ (*pckm)->aead_ctx.native_handle = NULL;
+ (*pckm)->pkt_num = -1;
+ (*pckm)->use_count = 0;
+ (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE;
+
+ return 0;
+}
+
+void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem) {
+ if (ckm == NULL) {
+ return;
+ }
+
+ ngtcp2_mem_free(mem, ckm);
+}
+
+void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
+ int64_t pkt_num) {
+ size_t i;
+ uint64_t n;
+
+ memcpy(dest, iv, ivlen);
+ n = ngtcp2_htonl64((uint64_t)pkt_num);
+
+ for (i = 0; i < 8; ++i) {
+ dest[ivlen - 8 + i] ^= ((uint8_t *)&n)[i];
+ }
+}
+
+/*
+ * varint_paramlen returns the length of a single transport parameter
+ * which has variable integer in its parameter.
+ */
+static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t param) {
+ size_t valuelen = ngtcp2_put_varint_len(param);
+ return ngtcp2_put_varint_len(id) + ngtcp2_put_varint_len(valuelen) + valuelen;
+}
+
+/*
+ * write_varint_param writes parameter |id| of the given |value| in
+ * varint encoding. It returns p + the number of bytes written.
+ */
+static uint8_t *write_varint_param(uint8_t *p, ngtcp2_transport_param_id id,
+ uint64_t value) {
+ p = ngtcp2_put_varint(p, id);
+ p = ngtcp2_put_varint(p, ngtcp2_put_varint_len(value));
+ return ngtcp2_put_varint(p, value);
+}
+
+/*
+ * cid_paramlen returns the length of a single transport parameter
+ * which has |cid| as value.
+ */
+static size_t cid_paramlen(ngtcp2_transport_param_id id,
+ const ngtcp2_cid *cid) {
+ return ngtcp2_put_varint_len(id) + ngtcp2_put_varint_len(cid->datalen) +
+ cid->datalen;
+}
+
+/*
+ * write_cid_param writes parameter |id| of the given |cid|. It
+ * returns p + the number of bytes written.
+ */
+static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id,
+ const ngtcp2_cid *cid) {
+ assert(cid->datalen == 0 || cid->datalen >= NGTCP2_MIN_CIDLEN);
+ assert(cid->datalen <= NGTCP2_MAX_CIDLEN);
+
+ p = ngtcp2_put_varint(p, id);
+ p = ngtcp2_put_varint(p, cid->datalen);
+ if (cid->datalen) {
+ p = ngtcp2_cpymem(p, cid->data, cid->datalen);
+ }
+ return p;
+}
+
+ngtcp2_ssize
+ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen,
+ ngtcp2_transport_params_type exttype,
+ 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;
+
+ switch (exttype) {
+ case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO:
+ break;
+ case NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS:
+ len +=
+ cid_paramlen(NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID,
+ &params->original_dcid);
+
+ if (params->stateless_reset_token_present) {
+ len +=
+ ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) +
+ ngtcp2_put_varint_len(NGTCP2_STATELESS_RESET_TOKENLEN) +
+ NGTCP2_STATELESS_RESET_TOKENLEN;
+ }
+ if (params->preferred_address_present) {
+ assert(params->preferred_address.cid.datalen >= NGTCP2_MIN_CIDLEN);
+ assert(params->preferred_address.cid.datalen <= NGTCP2_MAX_CIDLEN);
+ preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ +
+ 16 /* ipv6Address */ + 2 /* ipv6Port */
+ + 1 +
+ params->preferred_address.cid.datalen /* CID */ +
+ NGTCP2_STATELESS_RESET_TOKENLEN;
+ len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) +
+ ngtcp2_put_varint_len(preferred_addrlen) + preferred_addrlen;
+ }
+ if (params->retry_scid_present) {
+ len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID,
+ &params->retry_scid);
+ }
+ break;
+ default:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID,
+ &params->initial_scid);
+
+ if (params->initial_max_stream_data_bidi_local) {
+ len += varint_paramlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ params->initial_max_stream_data_bidi_local);
+ }
+ if (params->initial_max_stream_data_bidi_remote) {
+ len += varint_paramlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ params->initial_max_stream_data_bidi_remote);
+ }
+ if (params->initial_max_stream_data_uni) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI,
+ params->initial_max_stream_data_uni);
+ }
+ if (params->initial_max_data) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA,
+ params->initial_max_data);
+ }
+ if (params->initial_max_streams_bidi) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI,
+ params->initial_max_streams_bidi);
+ }
+ if (params->initial_max_streams_uni) {
+ 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) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
+ params->max_udp_payload_size);
+ }
+ if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT,
+ params->ack_delay_exponent);
+ }
+ if (params->disable_active_migration) {
+ len +=
+ ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) +
+ ngtcp2_put_varint_len(0);
+ }
+ if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY,
+ params->max_ack_delay / NGTCP2_MILLISECONDS);
+ }
+ if (params->max_idle_timeout) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT,
+ params->max_idle_timeout / NGTCP2_MILLISECONDS);
+ }
+ if (params->active_connection_id_limit &&
+ params->active_connection_id_limit !=
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT,
+ params->active_connection_id_limit);
+ }
+ if (params->max_datagram_frame_size) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
+ params->max_datagram_frame_size);
+ }
+
+ if (destlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = dest;
+
+ if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ p = write_cid_param(
+ p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID,
+ &params->original_dcid);
+
+ if (params->stateless_reset_token_present) {
+ p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN);
+ p = ngtcp2_put_varint(p, sizeof(params->stateless_reset_token));
+ p = ngtcp2_cpymem(p, params->stateless_reset_token,
+ sizeof(params->stateless_reset_token));
+ }
+ if (params->preferred_address_present) {
+ 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);
+
+ 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);
+
+ *p++ = (uint8_t)params->preferred_address.cid.datalen;
+ if (params->preferred_address.cid.datalen) {
+ p = ngtcp2_cpymem(p, params->preferred_address.cid.data,
+ params->preferred_address.cid.datalen);
+ }
+ p = ngtcp2_cpymem(
+ p, params->preferred_address.stateless_reset_token,
+ sizeof(params->preferred_address.stateless_reset_token));
+ }
+ if (params->retry_scid_present) {
+ p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID,
+ &params->retry_scid);
+ }
+ }
+
+ p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID,
+ &params->initial_scid);
+
+ if (params->initial_max_stream_data_bidi_local) {
+ p = write_varint_param(
+ p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ params->initial_max_stream_data_bidi_local);
+ }
+
+ if (params->initial_max_stream_data_bidi_remote) {
+ p = write_varint_param(
+ p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ params->initial_max_stream_data_bidi_remote);
+ }
+
+ if (params->initial_max_stream_data_uni) {
+ p = write_varint_param(p,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI,
+ params->initial_max_stream_data_uni);
+ }
+
+ if (params->initial_max_data) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA,
+ params->initial_max_data);
+ }
+
+ if (params->initial_max_streams_bidi) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI,
+ params->initial_max_streams_bidi);
+ }
+
+ if (params->initial_max_streams_uni) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
+ params->initial_max_streams_uni);
+ }
+
+ if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
+ params->max_udp_payload_size);
+ }
+
+ if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT,
+ params->ack_delay_exponent);
+ }
+
+ if (params->disable_active_migration) {
+ p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION);
+ p = ngtcp2_put_varint(p, 0);
+ }
+
+ if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY,
+ params->max_ack_delay / NGTCP2_MILLISECONDS);
+ }
+
+ if (params->max_idle_timeout) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT,
+ params->max_idle_timeout / NGTCP2_MILLISECONDS);
+ }
+
+ if (params->active_connection_id_limit &&
+ params->active_connection_id_limit !=
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT,
+ params->active_connection_id_limit);
+ }
+
+ if (params->max_datagram_frame_size) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
+ params->max_datagram_frame_size);
+ }
+
+ assert((size_t)(p - dest) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+/*
+ * decode_varint decodes a single varint from the buffer pointed by
+ * |p| of length |end - p|. If it decodes an integer successfully, it
+ * stores the integer in |*pdest| and returns 0. Otherwise it returns
+ * -1.
+ */
+static ngtcp2_ssize decode_varint(uint64_t *pdest, const uint8_t *p,
+ const uint8_t *end) {
+ size_t len;
+
+ if (p == end) {
+ return -1;
+ }
+
+ len = ngtcp2_get_varint_len(p);
+ if ((uint64_t)(end - p) < len) {
+ return -1;
+ }
+
+ *pdest = ngtcp2_get_varint(&len, p);
+
+ return (ngtcp2_ssize)len;
+}
+
+/*
+ * decode_varint_param decodes length prefixed value from the buffer
+ * pointed by |p| of length |end - p|. The length and value are
+ * encoded in varint form. If it decodes a value successfully, it
+ * stores the value in |*pdest| and returns 0. Otherwise it returns
+ * -1.
+ */
+static ngtcp2_ssize decode_varint_param(uint64_t *pdest, const uint8_t *p,
+ const uint8_t *end) {
+ const uint8_t *begin = p;
+ ngtcp2_ssize nread;
+ uint64_t valuelen;
+ size_t n;
+
+ nread = decode_varint(&valuelen, p, end);
+ if (nread < 0) {
+ return -1;
+ }
+
+ p += nread;
+
+ if (p == end) {
+ return -1;
+ }
+
+ if ((uint64_t)(end - p) < valuelen) {
+ return -1;
+ }
+
+ if (ngtcp2_get_varint_len(p) != valuelen) {
+ return -1;
+ }
+
+ *pdest = ngtcp2_get_varint(&n, p);
+
+ p += valuelen;
+
+ return (ngtcp2_ssize)(p - begin);
+}
+
+/*
+ * decode_cid_param decodes length prefixed ngtcp2_cid from the buffer
+ * pointed by |p| of length |end - p|. The length is encoded in
+ * varint form. If it decodes a value successfully, it stores the
+ * value in |*pdest| and returns the number of bytes read. Otherwise
+ * it returns -1.
+ */
+static ngtcp2_ssize decode_cid_param(ngtcp2_cid *pdest, const uint8_t *p,
+ const uint8_t *end) {
+ const uint8_t *begin = p;
+ uint64_t valuelen;
+ ngtcp2_ssize nread = decode_varint(&valuelen, p, end);
+
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ p += nread;
+
+ if ((valuelen != 0 && valuelen < NGTCP2_MIN_CIDLEN) ||
+ valuelen > NGTCP2_MAX_CIDLEN || (size_t)(end - p) < valuelen) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ ngtcp2_cid_init(pdest, p, (size_t)valuelen);
+
+ p += valuelen;
+
+ 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) {
+ const uint8_t *p, *end;
+ size_t len;
+ uint64_t param_type;
+ uint64_t valuelen;
+ ngtcp2_ssize nread;
+ int initial_scid_present = 0;
+ int original_dcid_present = 0;
+
+ p = data;
+ end = data + datalen;
+
+ /* Set default values */
+ memset(params, 0, sizeof(*params));
+ params->initial_max_streams_bidi = 0;
+ params->initial_max_streams_uni = 0;
+ 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->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ params->stateless_reset_token_present = 0;
+ params->preferred_address_present = 0;
+ params->disable_active_migration = 0;
+ params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
+ params->max_idle_timeout = 0;
+ params->active_connection_id_limit =
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params->retry_scid_present = 0;
+ params->max_datagram_frame_size = 0;
+ 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));
+
+ if (datalen == 0) {
+ return 0;
+ }
+
+ for (; (size_t)(end - p) >= 2;) {
+ nread = decode_varint(&param_type, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+
+ switch (param_type) {
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:
+ nread = decode_varint_param(&params->initial_max_stream_data_bidi_local,
+ p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:
+ nread = decode_varint_param(&params->initial_max_stream_data_bidi_remote,
+ p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI:
+ nread = decode_varint_param(&params->initial_max_stream_data_uni, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA:
+ nread = decode_varint_param(&params->initial_max_data, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI:
+ nread = decode_varint_param(&params->initial_max_streams_bidi, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->initial_max_streams_bidi > NGTCP2_MAX_STREAMS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI:
+ nread = decode_varint_param(&params->initial_max_streams_uni, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->initial_max_streams_uni > NGTCP2_MAX_STREAMS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT:
+ nread = decode_varint_param(&params->max_idle_timeout, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ params->max_idle_timeout *= NGTCP2_MILLISECONDS;
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE:
+ nread = decode_varint_param(&params->max_udp_payload_size, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ nread = decode_varint(&valuelen, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ if ((size_t)valuelen != sizeof(params->stateless_reset_token)) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if ((size_t)(end - p) < sizeof(params->stateless_reset_token)) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ memcpy(params->stateless_reset_token, p,
+ sizeof(params->stateless_reset_token));
+ params->stateless_reset_token_present = 1;
+
+ p += sizeof(params->stateless_reset_token);
+ break;
+ case NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT:
+ nread = decode_varint_param(&params->ack_delay_exponent, p, end);
+ if (nread < 0 || params->ack_delay_exponent > 20) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ 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;
+ }
+ len = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ +
+ 2 /* ipv6Port */
+ + 1 /* cid length */ + NGTCP2_STATELESS_RESET_TOKENLEN;
+ if (valuelen < len) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ memcpy(params->preferred_address.ipv4_addr, p,
+ sizeof(params->preferred_address.ipv4_addr));
+ p += sizeof(params->preferred_address.ipv4_addr);
+ params->preferred_address.ipv4_port = ngtcp2_get_uint16(p);
+ p += sizeof(uint16_t);
+
+ 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);
+
+ /* cid */
+ params->preferred_address.cid.datalen = *p++;
+ len += params->preferred_address.cid.datalen;
+ if (valuelen != len ||
+ params->preferred_address.cid.datalen > NGTCP2_MAX_CIDLEN ||
+ params->preferred_address.cid.datalen < NGTCP2_MIN_CIDLEN) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->preferred_address.cid.datalen) {
+ memcpy(params->preferred_address.cid.data, p,
+ params->preferred_address.cid.datalen);
+ p += params->preferred_address.cid.datalen;
+ }
+
+ /* stateless reset token */
+ memcpy(params->preferred_address.stateless_reset_token, p,
+ sizeof(params->preferred_address.stateless_reset_token));
+ p += sizeof(params->preferred_address.stateless_reset_token);
+ params->preferred_address_present = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION:
+ nread = decode_varint(&valuelen, p, end);
+ if (nread < 0 || valuelen != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ params->disable_active_migration = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ nread = decode_cid_param(&params->original_dcid, p, end);
+ if (nread < 0) {
+ return (int)nread;
+ }
+ original_dcid_present = 1;
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ nread = decode_cid_param(&params->retry_scid, p, end);
+ if (nread < 0) {
+ return (int)nread;
+ }
+ params->retry_scid_present = 1;
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID:
+ nread = decode_cid_param(&params->initial_scid, p, end);
+ if (nread < 0) {
+ return (int)nread;
+ }
+ initial_scid_present = 1;
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY:
+ nread = decode_varint_param(&params->max_ack_delay, p, end);
+ if (nread < 0 || params->max_ack_delay >= 16384) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ params->max_ack_delay *= NGTCP2_MILLISECONDS;
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT:
+ nread = decode_varint_param(&params->active_connection_id_limit, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE:
+ nread = decode_varint_param(&params->max_datagram_frame_size, p, end);
+ if (nread < 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += nread;
+ break;
+ default:
+ /* Ignore unknown parameter */
+ 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;
+ }
+ p += valuelen;
+ break;
+ }
+ }
+
+ if (end - p != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ if (!initial_scid_present ||
+ (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS &&
+ !original_dcid_present)) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
+
+ return 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h
new file mode 100644
index 0000000000..6e6f12a095
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h
@@ -0,0 +1,103 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_H
+#define NGTCP2_CRYPTO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+/* NGTCP2_INITIAL_AEAD_OVERHEAD is an overhead of AEAD used by Initial
+ packets. Because QUIC uses AEAD_AES_128_GCM, the overhead is 16
+ bytes. */
+#define NGTCP2_INITIAL_AEAD_OVERHEAD 16
+
+/* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */
+#define NGTCP2_MAX_AEAD_OVERHEAD 16
+
+/* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00
+/* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is
+ set. */
+#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01
+
+typedef struct ngtcp2_crypto_km {
+ ngtcp2_vec secret;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_vec iv;
+ /* pkt_num is a packet number of a packet which uses this keying
+ material. For encryption key, it is the lowest packet number of
+ a packet. For decryption key, it is the lowest packet number of
+ a packet which can be decrypted with this keying material. */
+ int64_t pkt_num;
+ /* use_count is the number of encryption applied with this key.
+ This field is only used for tx key. */
+ uint64_t use_count;
+ /* flags is the bitwise OR of zero or more of
+ NGTCP2_CRYPTO_KM_FLAG_*. */
+ uint8_t flags;
+} ngtcp2_crypto_km;
+
+/*
+ * ngtcp2_crypto_km_new creates new ngtcp2_crypto_km object and
+ * assigns its pointer to |*pckm|. The |secret| of length
+ * |secretlen|, the |key| of length |keylen| and the |iv| of length
+ * |ivlen| are copied to |*pckm|. If |secretlen| == 0, the function
+ * assumes no secret is given which is acceptable. The sole reason to
+ * store secret is update keys. Only 1RTT key can be updated.
+ */
+int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_crypto_km_nocopy_new is similar to ngtcp2_crypto_km_new, but
+ * it does not copy secret, key and IV.
+ */
+int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen,
+ size_t ivlen, const ngtcp2_mem *mem);
+
+void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem);
+
+typedef struct ngtcp2_crypto_cc {
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_cipher hp;
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ ngtcp2_encrypt encrypt;
+ ngtcp2_decrypt decrypt;
+ ngtcp2_hp_mask hp_mask;
+} ngtcp2_crypto_cc;
+
+void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
+ int64_t pkt_num);
+
+#endif /* NGTCP2_CRYPTO_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c
new file mode 100644
index 0000000000..bd15e0988b
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c
@@ -0,0 +1,144 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_err.h"
+
+const char *ngtcp2_strerror(int liberr) {
+ switch (liberr) {
+ case 0:
+ return "NO_ERROR";
+ case NGTCP2_ERR_INVALID_ARGUMENT:
+ return "ERR_INVALID_ARGUMENT";
+ case NGTCP2_ERR_NOBUF:
+ return "ERR_NOBUF";
+ case NGTCP2_ERR_PROTO:
+ return "ERR_PROTO";
+ case NGTCP2_ERR_INVALID_STATE:
+ return "ERR_INVALID_STATE";
+ case NGTCP2_ERR_ACK_FRAME:
+ return "ERR_ACK_FRAME";
+ case NGTCP2_ERR_STREAM_ID_BLOCKED:
+ return "ERR_STREAM_ID_BLOCKED";
+ case NGTCP2_ERR_STREAM_IN_USE:
+ return "ERR_STREAM_IN_USE";
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
+ return "ERR_STREAM_DATA_BLOCKED";
+ case NGTCP2_ERR_FLOW_CONTROL:
+ return "ERR_FLOW_CONTROL";
+ case NGTCP2_ERR_CONNECTION_ID_LIMIT:
+ return "ERR_CONNECTION_ID_LIMIT";
+ case NGTCP2_ERR_STREAM_LIMIT:
+ return "ERR_STREAM_LIMIT";
+ case NGTCP2_ERR_FINAL_SIZE:
+ return "ERR_FINAL_SIZE";
+ case NGTCP2_ERR_CRYPTO:
+ return "ERR_CRYPTO";
+ case NGTCP2_ERR_PKT_NUM_EXHAUSTED:
+ return "ERR_PKT_NUM_EXHAUSTED";
+ case NGTCP2_ERR_NOMEM:
+ return "ERR_NOMEM";
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ return "ERR_REQUIRED_TRANSPORT_PARAM";
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ 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_STREAM_SHUT_WR:
+ return "ERR_STREAM_SHUT_WR";
+ case NGTCP2_ERR_STREAM_NOT_FOUND:
+ return "ERR_STREAM_NOT_FOUND";
+ case NGTCP2_ERR_STREAM_STATE:
+ return "ERR_STREAM_STATE";
+ case NGTCP2_ERR_RECV_VERSION_NEGOTIATION:
+ return "ERR_RECV_VERSION_NEGOTIATION";
+ case NGTCP2_ERR_CLOSING:
+ return "ERR_CLOSING";
+ case NGTCP2_ERR_DRAINING:
+ return "ERR_DRAINING";
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ 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:
+ return "ERR_CALLBACK_FAILURE";
+ case NGTCP2_ERR_INTERNAL:
+ return "ERR_INTERNAL";
+ case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED:
+ return "ERR_CRYPTO_BUFFER_EXCEEDED";
+ case NGTCP2_ERR_WRITE_MORE:
+ return "ERR_WRITE_MORE";
+ case NGTCP2_ERR_RETRY:
+ return "ERR_RETRY";
+ case NGTCP2_ERR_DROP_CONN:
+ return "ERR_DROP_CONN";
+ case NGTCP2_ERR_AEAD_LIMIT_REACHED:
+ return "ERR_AEAD_LIMIT_REACHED";
+ case NGTCP2_ERR_NO_VIABLE_PATH:
+ return "ERR_NO_VIABLE_PATH";
+ default:
+ return "(unknown)";
+ }
+}
+
+int ngtcp2_err_is_fatal(int liberr) { return liberr < NGTCP2_ERR_FATAL; }
+
+uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
+ switch (liberr) {
+ case 0:
+ return NGTCP2_NO_ERROR;
+ case NGTCP2_ERR_ACK_FRAME:
+ case NGTCP2_ERR_FRAME_ENCODING:
+ return NGTCP2_FRAME_ENCODING_ERROR;
+ case NGTCP2_ERR_FLOW_CONTROL:
+ return NGTCP2_FLOW_CONTROL_ERROR;
+ case NGTCP2_ERR_CONNECTION_ID_LIMIT:
+ return NGTCP2_CONNECTION_ID_LIMIT_ERROR;
+ case NGTCP2_ERR_STREAM_LIMIT:
+ return NGTCP2_STREAM_LIMIT_ERROR;
+ case NGTCP2_ERR_FINAL_SIZE:
+ return NGTCP2_FINAL_SIZE_ERROR;
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ return NGTCP2_TRANSPORT_PARAMETER_ERROR;
+ case NGTCP2_ERR_INVALID_ARGUMENT:
+ return NGTCP2_INTERNAL_ERROR;
+ case NGTCP2_ERR_STREAM_STATE:
+ return NGTCP2_STREAM_STATE_ERROR;
+ case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED:
+ return NGTCP2_CRYPTO_BUFFER_EXCEEDED;
+ case NGTCP2_ERR_AEAD_LIMIT_REACHED:
+ return NGTCP2_AEAD_LIMIT_REACHED;
+ case NGTCP2_ERR_NO_VIABLE_PATH:
+ return NGTCP2_NO_VIABLE_PATH;
+ default:
+ return NGTCP2_PROTOCOL_VIOLATION;
+ }
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h
new file mode 100644
index 0000000000..9229f5425a
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h
@@ -0,0 +1,34 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ERR_H
+#define NGTCP2_ERR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#endif /* NGTCP2_ERR_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
new file mode 100644
index 0000000000..6e7f3b7e55
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
@@ -0,0 +1,129 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_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};
+
+ rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar,
+ sizeof(ngtcp2_range), mem);
+ if (rv != 0) {
+ return 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;
+}
+
+void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) {
+ if (gaptr == NULL) {
+ return;
+ }
+
+ ngtcp2_ksl_free(&gaptr->gap);
+}
+
+int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
+ int rv;
+ ngtcp2_range k, m, l, r, q = {offset, offset + datalen};
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+ m = ngtcp2_range_intersect(&q, &k);
+ if (!ngtcp2_range_len(&m)) {
+ break;
+ }
+
+ if (ngtcp2_range_eq(&k, &m)) {
+ ngtcp2_ksl_remove(&gaptr->gap, &it, &k);
+ continue;
+ }
+ ngtcp2_range_cut(&l, &r, &k, &m);
+ if (ngtcp2_range_len(&l)) {
+ ngtcp2_ksl_update_key(&gaptr->gap, &k, &l);
+
+ if (ngtcp2_range_len(&r)) {
+ rv = ngtcp2_ksl_insert(&gaptr->gap, &it, &r, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else if (ngtcp2_range_len(&r)) {
+ ngtcp2_ksl_update_key(&gaptr->gap, &k, &r);
+ }
+ ngtcp2_ksl_it_next(&it);
+ }
+ return 0;
+}
+
+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);
+ return r.begin;
+}
+
+ngtcp2_ksl_it 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);
+}
+
+int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
+ size_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);
+ 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_range r;
+
+ assert(!ngtcp2_ksl_it_end(&it));
+
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ ngtcp2_ksl_remove(&gaptr->gap, NULL, &r);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
new file mode 100644
index 0000000000..500d376008
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
@@ -0,0 +1,103 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_GAPTR_H
+#define NGTCP2_GAPTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_ksl.h"
+
+/*
+ * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX).
+ */
+typedef struct ngtcp2_gaptr {
+ /* gap maintains the range of offset which is not received
+ yet. Initially, its range is [0, UINT64_MAX). */
+ ngtcp2_ksl gap;
+ /* mem is custom memory allocator */
+ const ngtcp2_mem *mem;
+} 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);
+
+/*
+ * ngtcp2_gaptr_free frees resources allocated for |gaptr|.
+ */
+void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr);
+
+/*
+ * ngtcp2_gaptr_push adds new data of length |datalen| at the stream
+ * offset |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen);
+
+/*
+ * ngtcp2_gaptr_first_gap_offset returns the offset to the first gap.
+ * If there is no gap, it returns UINT64_MAX.
+ */
+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_ksl_it 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);
+
+/*
+ * ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if
+ * the range is pushed. This function assumes that at least one gap
+ * exists.
+ */
+void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr);
+
+#endif /* NGTCP2_GAPTR_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c
new file mode 100644
index 0000000000..f04806b4a8
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c
@@ -0,0 +1,86 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_idtr.h"
+
+#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;
+ }
+
+ idtr->server = server;
+
+ return 0;
+}
+
+void ngtcp2_idtr_free(ngtcp2_idtr *idtr) {
+ if (idtr == NULL) {
+ return;
+ }
+
+ ngtcp2_gaptr_free(&idtr->gap);
+}
+
+/*
+ * id_from_stream_id translates |stream_id| to id space used by
+ * ngtcp2_idtr.
+ */
+static uint64_t id_from_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2);
+}
+
+int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ if (ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1)) {
+ return NGTCP2_ERR_STREAM_IN_USE;
+ }
+
+ return ngtcp2_gaptr_push(&idtr->gap, q, 1);
+}
+
+int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ return ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1);
+}
+
+uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr) {
+ return ngtcp2_gaptr_first_gap_offset(&idtr->gap);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h
new file mode 100644
index 0000000000..1be64dc16e
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h
@@ -0,0 +1,95 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_IDTR_H
+#define NGTCP2_IDTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_gaptr.h"
+
+/*
+ * ngtcp2_idtr tracks the usage of stream ID.
+ */
+typedef struct ngtcp2_idtr {
+ /* gap maintains the range of ID which is not used yet. Initially,
+ its range is [0, UINT64_MAX). */
+ ngtcp2_gaptr gap;
+ /* server is nonzero if this object records server initiated stream
+ ID. */
+ int server;
+} ngtcp2_idtr;
+
+/*
+ * ngtcp2_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:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_idtr_free frees resources allocated for |idtr|.
+ */
+void ngtcp2_idtr_free(ngtcp2_idtr *idtr);
+
+/*
+ * ngtcp2_idtr_open claims that |stream_id| is in used.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_STREAM_IN_USE
+ * ID has already been used.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id);
+
+/*
+ * ngtcp2_idtr_open tells whether ID |stream_id| is in used or not.
+ *
+ * It returns nonzero if |stream_id| is used.
+ */
+int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id);
+
+/*
+ * ngtcp2_idtr_first_gap returns the first id of first gap. If there
+ * is no gap, it returns UINT64_MAX. The returned id is an id space
+ * used in this object internally, and not stream ID.
+ */
+uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr);
+
+#endif /* NGTCP2_IDTR_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c
new file mode 100644
index 0000000000..fd25e3514e
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c
@@ -0,0 +1,741 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ksl.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_range.h"
+
+static size_t ksl_nodelen(size_t keylen) {
+ return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
+ (size_t)~0xf;
+}
+
+static size_t ksl_blklen(size_t nodelen) {
+ return sizeof(ngtcp2_ksl_blk) + nodelen * NGTCP2_KSL_MAX_NBLK -
+ sizeof(uint64_t);
+}
+
+/*
+ * ksl_node_set_key sets |key| to |node|.
+ */
+static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node,
+ const void *key) {
+ memcpy(node->key, key, ksl->keylen);
+}
+
+int 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;
+ ksl->compar = compar;
+ ksl->keylen = keylen;
+ ksl->nodelen = nodelen;
+ ksl->n = 0;
+ ksl->mem = mem;
+
+ head = ksl->head;
+ head->next = head->prev = NULL;
+ head->n = 0;
+ head->leaf = 1;
+
+ return 0;
+}
+
+/*
+ * ksl_free_blk frees |blk| recursively.
+ */
+static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
+ size_t i;
+
+ if (!blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk);
+ }
+ }
+
+ ngtcp2_mem_free(ksl->mem, blk);
+}
+
+void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
+ if (!ksl) {
+ return;
+ }
+
+ ksl_free_blk(ksl, ksl->head);
+}
+
+/*
+ * ksl_split_blk splits |blk| into 2 ngtcp2_ksl_blk objects. The new
+ * ngtcp2_ksl_blk is always the "right" block.
+ *
+ * It returns the pointer to the ngtcp2_ksl_blk created which is the
+ * located at the right of |blk|, or NULL which indicates out of
+ * memory error.
+ */
+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));
+ if (rblk == NULL) {
+ return NULL;
+ }
+
+ rblk->next = blk->next;
+ blk->next = rblk;
+ if (rblk->next) {
+ rblk->next->prev = rblk;
+ } else if (ksl->back == blk) {
+ ksl->back = rblk;
+ }
+ rblk->prev = blk;
+ rblk->leaf = blk->leaf;
+
+ rblk->n = blk->n / 2;
+
+ memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n),
+ ksl->nodelen * rblk->n);
+
+ blk->n -= rblk->n;
+
+ assert(blk->n >= NGTCP2_KSL_MIN_NBLK);
+ assert(rblk->n >= NGTCP2_KSL_MIN_NBLK);
+
+ return rblk;
+}
+
+/*
+ * ksl_split_node splits a node included in |blk| at the position |i|
+ * into 2 adjacent nodes. The new node is always inserted at the
+ * position |i+1|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *node;
+ ngtcp2_ksl_blk *lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk, *rblk;
+
+ rblk = ksl_split_blk(ksl, lblk);
+ if (rblk == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ memmove(blk->nodes + (i + 2) * ksl->nodelen,
+ blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
+ node->blk = rblk;
+ ++blk->n;
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+
+ return 0;
+}
+
+/*
+ * ksl_split_head splits a head (root) block. It increases the height
+ * of skip list by 1.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_head(ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
+ ngtcp2_ksl_node *node;
+
+ rblk = ksl_split_blk(ksl, ksl->head);
+ if (rblk == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ lblk = ksl->head;
+
+ nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+ if (nhead == NULL) {
+ ngtcp2_mem_free(ksl->mem, rblk);
+ return NGTCP2_ERR_NOMEM;
+ }
+ nhead->next = nhead->prev = NULL;
+ nhead->n = 2;
+ nhead->leaf = 0;
+
+ node = ngtcp2_ksl_nth_node(ksl, nhead, 0);
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ node->blk = lblk;
+
+ node = ngtcp2_ksl_nth_node(ksl, nhead, 1);
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+ node->blk = rblk;
+
+ ksl->head = nhead;
+
+ return 0;
+}
+
+/*
+ * insert_node inserts a node whose key is |key| with the associated
+ * |data| at the index of |i|. This function assumes that the number
+ * of nodes contained by |blk| is strictly less than
+ * NGTCP2_KSL_MAX_NBLK.
+ */
+static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i,
+ const ngtcp2_ksl_key *key, void *data) {
+ ngtcp2_ksl_node *node;
+
+ assert(blk->n < NGTCP2_KSL_MAX_NBLK);
+
+ memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
+ ksl->nodelen * (blk->n - i));
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, key);
+ node->data = data;
+
+ ++blk->n;
+}
+
+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;
+ 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;
+ }
+ }
+
+ return (size_t)right;
+}
+
+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_node *node;
+ size_t i;
+ int rv;
+
+ if (blk->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_head(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ blk = ksl->head;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i < blk->n &&
+ !ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = ngtcp2_ksl_end(ksl);
+ }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ ksl_insert_node(ksl, blk, i, key, data);
+ ++ksl->n;
+ if (it) {
+ ngtcp2_ksl_it_init(it, ksl, blk, i);
+ }
+ return 0;
+ }
+
+ if (i == blk->n) {
+ /* This insertion extends the largest key in this subtree. */
+ for (; !blk->leaf;) {
+ node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1);
+ if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, blk->n - 1);
+ if (rv != 0) {
+ return rv;
+ }
+ node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1);
+ }
+ ksl_node_set_key(ksl, node, key);
+ blk = node->blk;
+ }
+ ksl_insert_node(ksl, blk, blk->n, key, data);
+ ++ksl->n;
+ if (it) {
+ ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1);
+ }
+ return 0;
+ }
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, i);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) {
+ node = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
+ if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) {
+ ksl_node_set_key(ksl, node, key);
+ }
+ }
+ }
+
+ blk = node->blk;
+ }
+}
+
+/*
+ * ksl_remove_node removes the node included in |blk| at the index of
+ * |i|.
+ */
+static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ --blk->n;
+}
+
+/*
+ * ksl_merge_node merges 2 nodes which are the nodes at the index of
+ * |i| and |i + 1|.
+ *
+ * If |blk| is the direct descendant of head (root) block and the head
+ * block contains just 2 nodes, the merged block becomes head block,
+ * which decreases the height of |ksl| by 1.
+ *
+ * This function returns the pointer to the merged block.
+ */
+static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ size_t i) {
+ ngtcp2_ksl_blk *lblk, *rblk;
+
+ assert(i + 1 < blk->n);
+
+ lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk;
+ rblk = ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk;
+
+ assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK);
+
+ memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
+ ksl->nodelen * rblk->n);
+
+ lblk->n += rblk->n;
+ lblk->next = rblk->next;
+ if (lblk->next) {
+ lblk->next->prev = lblk;
+ } else if (ksl->back == rblk) {
+ ksl->back = lblk;
+ }
+
+ ngtcp2_mem_free(ksl->mem, rblk);
+
+ if (ksl->head == blk && blk->n == 2) {
+ ngtcp2_mem_free(ksl->mem, ksl->head);
+ ksl->head = lblk;
+ } else {
+ ksl_remove_node(ksl, blk, i + 1);
+ ksl_node_set_key(ksl, ngtcp2_ksl_nth_node(ksl, blk, i),
+ ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ }
+
+ return lblk;
+}
+
+/*
+ * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i - 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible.
+ */
+static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i > 0);
+
+ lnode = ngtcp2_ksl_nth_node(ksl, blk, i - 1);
+ rnode = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ assert(lnode->blk->n < NGTCP2_KSL_MAX_NBLK);
+ assert(rnode->blk->n > NGTCP2_KSL_MIN_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - lnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n <= NGTCP2_KSL_MAX_NBLK - n);
+ assert(rnode->blk->n >= NGTCP2_KSL_MIN_NBLK + n);
+
+ memcpy(lnode->blk->nodes + ksl->nodelen * lnode->blk->n, rnode->blk->nodes,
+ ksl->nodelen * n);
+
+ lnode->blk->n += (uint32_t)n;
+ rnode->blk->n -= (uint32_t)n;
+
+ ksl_node_set_key(
+ ksl, lnode, ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+
+ memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen * n,
+ ksl->nodelen * rnode->blk->n);
+}
+
+/*
+ * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i + 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible..
+ */
+static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i < blk->n - 1);
+
+ lnode = ngtcp2_ksl_nth_node(ksl, blk, i);
+ rnode = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
+
+ assert(lnode->blk->n > NGTCP2_KSL_MIN_NBLK);
+ assert(rnode->blk->n < NGTCP2_KSL_MAX_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - rnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n >= NGTCP2_KSL_MIN_NBLK + n);
+ assert(rnode->blk->n <= NGTCP2_KSL_MAX_NBLK - n);
+
+ memmove(rnode->blk->nodes + ksl->nodelen * n, rnode->blk->nodes,
+ ksl->nodelen * rnode->blk->n);
+
+ rnode->blk->n += (uint32_t)n;
+ lnode->blk->n -= (uint32_t)n;
+
+ memcpy(rnode->blk->nodes, lnode->blk->nodes + ksl->nodelen * lnode->blk->n,
+ ksl->nodelen * n);
+
+ ksl_node_set_key(
+ ksl, lnode, ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+}
+
+/*
+ * key_equal returns nonzero if |lhs| and |rhs| are equal using the
+ * function |compar|.
+ */
+static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ return !compar(lhs, rhs) && !compar(rhs, lhs);
+}
+
+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 (!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) {
+ blk = ksl_merge_node(ksl, ksl->head, 0);
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (i == blk->n) {
+ if (it) {
+ *it = ngtcp2_ksl_end(ksl);
+ }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (blk->leaf) {
+ if (ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = ngtcp2_ksl_end(ksl);
+ }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ ksl_remove_node(ksl, blk, i);
+ --ksl->n;
+ if (it) {
+ if (blk->n == i && blk->next) {
+ ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ ngtcp2_ksl_it_init(it, ksl, blk, i);
+ }
+ }
+ return 0;
+ }
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ blk = node->blk;
+ continue;
+ }
+
+ assert(node->blk->n == NGTCP2_KSL_MIN_NBLK);
+
+ if (i + 1 < blk->n &&
+ ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ ksl_shift_left(ksl, blk, i + 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i > 0 &&
+ ngtcp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ ksl_shift_right(ksl, blk, i - 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i + 1 < blk->n) {
+ blk = ksl_merge_node(ksl, blk, i);
+ continue;
+ }
+
+ assert(i > 0);
+
+ blk = ksl_merge_node(ksl, blk, i - 1);
+ }
+}
+
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_it it;
+ size_t i;
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key,
+ ngtcp2_ksl_compar compar) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_it it;
+ size_t i;
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
+ const ngtcp2_ksl_key *new_key) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_node *node;
+ size_t i;
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
+
+ assert(i < blk->n);
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ if (blk->leaf) {
+ assert(key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key));
+ ksl_node_set_key(ksl, node, new_key);
+ return;
+ }
+
+ if (key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key) ||
+ ksl->compar((ngtcp2_ksl_key *)node->key, new_key)) {
+ ksl_node_set_key(ksl, node, new_key);
+ }
+
+ blk = node->blk;
+ }
+}
+
+static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) {
+ size_t i;
+ ngtcp2_ksl_node *node;
+
+ fprintf(stderr, "LV=%zu n=%u\n", level, blk->n);
+
+ if (blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key);
+ }
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ for (i = 0; i < blk->n; ++i) {
+ ksl_print(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk, level + 1);
+ }
+}
+
+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);
+ }
+ }
+
+ ksl->front = ksl->back = ksl->head;
+ ksl->n = 0;
+
+ head = ksl->head;
+
+ head->next = head->prev = NULL;
+ head->n = 0;
+ head->leaf = 1;
+}
+
+void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { 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);
+ 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);
+ return it;
+}
+
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
+ ngtcp2_ksl_blk *blk, size_t i) {
+ it->ksl = ksl;
+ it->blk = blk;
+ 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));
+
+ if (it->i == 0) {
+ it->blk = it->blk->prev;
+ it->i = it->blk->n - 1;
+ } else {
+ --it->i;
+ }
+}
+
+int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) {
+ return it->i == 0 && it->blk->prev == NULL;
+}
+
+int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = lhs, *b = rhs;
+ return a->begin < b->begin;
+}
+
+int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = lhs, *b = rhs;
+ return a->begin < b->begin &&
+ !(ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end));
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h
new file mode 100644
index 0000000000..f24487d03e
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h
@@ -0,0 +1,329 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_KSL_H
+#define NGTCP2_KSL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * Skip List using single key instead of range.
+ */
+
+#define NGTCP2_KSL_DEGR 16
+/* NGTCP2_KSL_MAX_NBLK is the maximum number of nodes which a single
+ block can contain. */
+#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR - 1)
+/* NGTCP2_KSL_MIN_NBLK is the minimum number of nodes which a single
+ block other than root must contains. */
+#define NGTCP2_KSL_MIN_NBLK (NGTCP2_KSL_DEGR - 1)
+
+/*
+ * ngtcp2_ksl_key represents key in ngtcp2_ksl.
+ */
+typedef void ngtcp2_ksl_key;
+
+typedef struct ngtcp2_ksl_node ngtcp2_ksl_node;
+
+typedef struct ngtcp2_ksl_blk ngtcp2_ksl_blk;
+
+/*
+ * ngtcp2_ksl_node is a node which contains either ngtcp2_ksl_blk or
+ * opaque data. If a node is an internal node, it contains
+ * ngtcp2_ksl_blk. Otherwise, it has data. The key is stored at the
+ * location starting at key.
+ */
+struct ngtcp2_ksl_node {
+ union {
+ ngtcp2_ksl_blk *blk;
+ void *data;
+ };
+ union {
+ uint64_t align;
+ /* key is a buffer to include key associated to this node.
+ Because the length of key is unknown until ngtcp2_ksl_init is
+ called, the actual buffer will be allocated after this
+ field. */
+ uint8_t key[1];
+ };
+};
+
+/*
+ * 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];
+ };
+};
+
+/*
+ * ngtcp2_ksl_compar is a function type which returns nonzero if key
+ * |lhs| should be placed before |rhs|. It returns 0 otherwise.
+ */
+typedef int (*ngtcp2_ksl_compar)(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
+typedef struct ngtcp2_ksl ngtcp2_ksl;
+
+typedef struct ngtcp2_ksl_it ngtcp2_ksl_it;
+
+/*
+ * ngtcp2_ksl_it is a forward iterator to iterate nodes.
+ */
+struct ngtcp2_ksl_it {
+ const ngtcp2_ksl *ksl;
+ ngtcp2_ksl_blk *blk;
+ size_t i;
+};
+
+/*
+ * ngtcp2_ksl is a deterministic paged skip list.
+ */
+struct ngtcp2_ksl {
+ /* head points to the root block. */
+ ngtcp2_ksl_blk *head;
+ /* front points to the first leaf block. */
+ ngtcp2_ksl_blk *front;
+ /* back points to the last leaf block. */
+ ngtcp2_ksl_blk *back;
+ ngtcp2_ksl_compar compar;
+ size_t n;
+ /* keylen is the size of key */
+ size_t keylen;
+ /* 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);
+
+/*
+ * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is
+ * NULL, this function does nothing. It does not free the memory
+ * region pointed by |ksl| itself.
+ */
+void ngtcp2_ksl_free(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_insert inserts |key| with its associated |data|. On
+ * successful insertion, the iterator points to the inserted node is
+ * stored in |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |key| already exists.
+ */
+int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_key *key, void *data);
+
+/*
+ * ngtcp2_ksl_remove removes the |key| from |ksl|.
+ *
+ * This function assigns the iterator to |*it|, which points to the
+ * node which is located at the right next of the removed node if |it|
+ * is not NULL. If |key| is not found, no deletion takes place and
+ * the return value of ngtcp2_ksl_end(ksl) is assigned to |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |key| does not exist.
+ */
+int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ 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
+ * node, it returns the iterator which satisfies ngtcp2_ksl_it_end(it)
+ * != 0.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key);
+
+/*
+ * ngtcp2_ksl_lower_bound_compar works like ngtcp2_ksl_lower_bound,
+ * but it takes custom function |compar| to do lower bound search.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key,
+ ngtcp2_ksl_compar compar);
+
+/*
+ * ngtcp2_ksl_update_key replaces the key of nodes which has |old_key|
+ * with |new_key|. |new_key| must be strictly greater than the
+ * previous node and strictly smaller than the next node.
+ */
+void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
+ const ngtcp2_ksl_key *new_key);
+
+/*
+ * ngtcp2_ksl_begin returns the iterator which points to the first
+ * node. If there is no node in |ksl|, it returns the iterator which
+ * satisfies ngtcp2_ksl_it_end(it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_end returns the iterator which points to the node
+ * following the last node. The returned object satisfies
+ * ngtcp2_ksl_it_end(). If there is no node in |ksl|, it returns the
+ * iterator which satisfies ngtcp2_ksl_it_begin(it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_len returns the number of elements stored in |ksl|.
+ */
+size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_clear removes all elements stored in |ksl|.
+ */
+void ngtcp2_ksl_clear(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_nth_node returns the |n|th node under |blk|.
+ */
+#define ngtcp2_ksl_nth_node(KSL, BLK, N) \
+ ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
+
+/*
+ * ngtcp2_ksl_print prints its internal state in stderr. It assumes
+ * that the key is of type int64_t. This function should be used for
+ * the debugging purpose only.
+ */
+void ngtcp2_ksl_print(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_it_init initializes |it|.
+ */
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
+ ngtcp2_ksl_blk *blk, size_t i);
+
+/*
+ * ngtcp2_ksl_it_get returns the data associated to the node which
+ * |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);
+
+/*
+ * ngtcp2_ksl_it_next advances the iterator by one. It is undefined
+ * if this function is called when ngtcp2_ksl_it_end(it) returns
+ * nonzero.
+ */
+#define ngtcp2_ksl_it_next(IT) \
+ (++(IT)->i == (IT)->blk->n && (IT)->blk->next \
+ ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
+ : 0)
+
+/*
+ * ngtcp2_ksl_it_prev moves backward the iterator by one. It is
+ * undefined if this function is called when ngtcp2_ksl_it_begin(it)
+ * returns nonzero.
+ */
+void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it);
+
+/*
+ * ngtcp2_ksl_it_end returns nonzero if |it| points to the beyond the
+ * last node.
+ */
+#define ngtcp2_ksl_it_end(IT) \
+ ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
+
+/*
+ * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first
+ * node. |it| might satisfy both ngtcp2_ksl_it_begin(&it) and
+ * ngtcp2_ksl_it_end(&it) if the skip list has no node.
+ */
+int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it);
+
+/*
+ * ngtcp2_ksl_key returns the key of the node which |it| points to.
+ * It is undefined to call this function when ngtcp2_ksl_it_end(it)
+ * returns nonzero.
+ */
+#define ngtcp2_ksl_it_key(IT) \
+ ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
+
+/*
+ * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar.
+ * lhs->ptr and rhs->ptr must point to ngtcp2_range object and the
+ * function returns nonzero if (const ngtcp2_range *)(lhs->ptr)->begin
+ * < (const ngtcp2_range *)(rhs->ptr)->begin.
+ */
+int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
+/*
+ * ngtcp2_ksl_range_exclusive_compar is an implementation of
+ * ngtcp2_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * ngtcp2_range object and the function returns nonzero if (const
+ * ngtcp2_range *)(lhs->ptr)->begin < (const ngtcp2_range
+ * *)(rhs->ptr)->begin and the 2 ranges do not intersect.
+ */
+int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
+#endif /* NGTCP2_KSL_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c
new file mode 100644
index 0000000000..0259404d3e
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c
@@ -0,0 +1,767 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_log.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <assert.h>
+#include <string.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_macro.h"
+
+void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
+ ngtcp2_printf log_printf, ngtcp2_tstamp ts,
+ void *user_data) {
+ if (scid) {
+ ngtcp2_encode_hex(log->scid, scid->data, scid->datalen);
+ } else {
+ log->scid[0] = '\0';
+ }
+ log->log_printf = log_printf;
+ log->ts = log->last_ts = ts;
+ log->user_data = user_data;
+}
+
+/*
+ * # Log header
+ *
+ * <LEVEL><TIMESTAMP> <SCID> <EVENT>
+ *
+ * <LEVEL>:
+ * Log level. I=Info, W=Warning, E=Error
+ *
+ * <TIMESTAMP>:
+ * Timestamp relative to ngtcp2_log.ts field in milliseconds
+ * resolution.
+ *
+ * <SCID>:
+ * Source Connection ID in hex string.
+ *
+ * <EVENT>:
+ * Event. pkt=packet, frm=frame, rcv=recovery, cry=crypto,
+ * con=connection(catch all)
+ *
+ * # Frame event
+ *
+ * <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>)
+ *
+ * <DIR>:
+ * Flow direction. tx=transmission, rx=reception
+ *
+ * <PKN>:
+ * Packet number.
+ *
+ * <PKTNAME>:
+ * Packet name. (e.g., Initial, Handshake, S01)
+ *
+ * <PKTTYPE>:
+ * Packet type in hex string.
+ *
+ * <FRAMENAME>:
+ * Frame name. (e.g., STREAM, ACK, PING)
+ *
+ * <FRAMETYPE>:
+ * Frame type in hex string.
+ */
+
+#define NGTCP2_LOG_BUFLEN 4096
+
+/* 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_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
+
+#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
+
+#define NGTCP2_LOG_TP_HD_FIELDS \
+ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry"
+
+static const char *strerrorcode(uint64_t error_code) {
+ switch (error_code) {
+ case NGTCP2_NO_ERROR:
+ return "NO_ERROR";
+ case NGTCP2_INTERNAL_ERROR:
+ return "INTERNAL_ERROR";
+ case NGTCP2_CONNECTION_REFUSED:
+ return "CONNECTION_REFUSED";
+ case NGTCP2_FLOW_CONTROL_ERROR:
+ return "FLOW_CONTROL_ERROR";
+ case NGTCP2_STREAM_LIMIT_ERROR:
+ return "STREAM_LIMIT_ERROR";
+ case NGTCP2_STREAM_STATE_ERROR:
+ return "STREAM_STATE_ERROR";
+ case NGTCP2_FINAL_SIZE_ERROR:
+ return "FINAL_SIZE_ERROR";
+ case NGTCP2_FRAME_ENCODING_ERROR:
+ return "FRAME_ENCODING_ERROR";
+ case NGTCP2_TRANSPORT_PARAMETER_ERROR:
+ return "TRANSPORT_PARAMETER_ERROR";
+ case NGTCP2_CONNECTION_ID_LIMIT_ERROR:
+ return "CONNECTION_ID_LIMIT_ERROR";
+ case NGTCP2_PROTOCOL_VIOLATION:
+ return "PROTOCOL_VIOLATION";
+ case NGTCP2_INVALID_TOKEN:
+ return "INVALID_TOKEN";
+ case NGTCP2_APPLICATION_ERROR:
+ return "APPLICATION_ERROR";
+ case NGTCP2_CRYPTO_BUFFER_EXCEEDED:
+ return "CRYPTO_BUFFER_EXCEEDED";
+ case NGTCP2_KEY_UPDATE_ERROR:
+ return "KEY_UPDATE_ERROR";
+ default:
+ if (0x100u <= error_code && error_code <= 0x1ffu) {
+ return "CRYPTO_ERROR";
+ }
+ return "(unknown)";
+ }
+}
+
+static const char *strapperrorcode(uint64_t app_error_code) {
+ (void)app_error_code;
+ return "(unknown)";
+}
+
+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:
+ return "Retry";
+ case NGTCP2_PKT_HANDSHAKE:
+ return "Handshake";
+ case NGTCP2_PKT_0RTT:
+ return "0RTT";
+ default:
+ return "(unknown)";
+ }
+}
+
+static const char *strpkttype(const ngtcp2_pkt_hd *hd) {
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ return strpkttype_long(hd->type);
+ }
+ return "Short";
+}
+
+static const char *strevent(ngtcp2_log_event ev) {
+ switch (ev) {
+ case NGTCP2_LOG_EVENT_CON:
+ return "con";
+ case NGTCP2_LOG_EVENT_PKT:
+ return "pkt";
+ case NGTCP2_LOG_EVENT_FRM:
+ return "frm";
+ case NGTCP2_LOG_EVENT_RCV:
+ return "rcv";
+ case NGTCP2_LOG_EVENT_CRY:
+ return "cry";
+ case NGTCP2_LOG_EVENT_PTV:
+ return "ptv";
+ case NGTCP2_LOG_EVENT_NONE:
+ default:
+ return "non";
+ }
+}
+
+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);
+}
+
+static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_ack *fr, const char *dir) {
+ int64_t largest_ack, min_ack;
+ size_t i;
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) largest_ack=%" PRId64
+ " ack_delay=%" PRIu64 "(%" PRIu64
+ ") ack_block_count=%zu"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->largest_ack,
+ fr->ack_delay_unscaled / NGTCP2_MILLISECONDS, fr->ack_delay,
+ fr->num_blks);
+
+ largest_ack = fr->largest_ack;
+ min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen;
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64
+ "] block_count=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, min_ack,
+ fr->first_ack_blklen);
+
+ for (i = 0; i < fr->num_blks; ++i) {
+ const ngtcp2_ack_blk *blk = &fr->blks[i];
+ largest_ack = min_ack - (int64_t)blk->gap - 2;
+ min_ack = largest_ack - (int64_t)blk->blklen;
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64
+ "] gap=%" PRIu64 " block_count=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack,
+ min_ack, blk->gap, blk->blklen);
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) ect0=%" PRIu64
+ " ect1=%" PRIu64 " ce=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->ecn.ect0,
+ fr->ecn.ect1, fr->ecn.ce);
+ }
+}
+
+static void log_fr_padding(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_padding *fr, const char *dir) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PADDING(0x%02x) len=%zu"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->len);
+}
+
+static void log_fr_reset_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_reset_stream *fr,
+ const char *dir) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " RESET_STREAM(0x%02x) id=0x%" PRIx64
+ " app_error_code=%s(0x%" PRIx64 ") final_size=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ strapperrorcode(fr->app_error_code), fr->app_error_code, fr->final_size);
+}
+
+static void log_fr_connection_close(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_connection_close *fr,
+ const char *dir) {
+ char reason[256];
+ size_t reasonlen = ngtcp2_min(sizeof(reason) - 1, fr->reasonlen);
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT
+ " CONNECTION_CLOSE(0x%02x) error_code=%s(0x%" PRIx64 ") "
+ "frame_type=%" PRIx64 " reason_len=%zu reason=[%s]"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ fr->type == NGTCP2_FRAME_CONNECTION_CLOSE
+ ? strerrorcode(fr->error_code)
+ : strapperrorcode(fr->error_code),
+ fr->error_code, fr->frame_type, fr->reasonlen,
+ ngtcp2_encode_printable_ascii(reason, fr->reason, reasonlen));
+}
+
+static void log_fr_max_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_max_data *fr, const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " MAX_DATA(0x%02x) max_data=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_data);
+}
+
+static void log_fr_max_stream_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_max_stream_data *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " MAX_STREAM_DATA(0x%02x) id=0x%" PRIx64
+ " max_stream_data=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ fr->max_stream_data);
+}
+
+static void log_fr_max_streams(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_max_streams *fr, const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " MAX_STREAMS(0x%02x) max_streams=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams);
+}
+
+static void log_fr_ping(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_ping *fr, const char *dir) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PING(0x%02x)"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type);
+}
+
+static void log_fr_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_data_blocked *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " DATA_BLOCKED(0x%02x) offset=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset);
+}
+
+static void log_fr_stream_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_stream_data_blocked *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " STREAM_DATA_BLOCKED(0x%02x) id=0x%" PRIx64
+ " offset=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ fr->offset);
+}
+
+static void log_fr_streams_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_streams_blocked *fr,
+ const char *dir) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02x) max_streams=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams);
+}
+
+static void log_fr_new_connection_id(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_new_connection_id *fr,
+ const char *dir) {
+ uint8_t buf[sizeof(fr->stateless_reset_token) * 2 + 1];
+ uint8_t cid[sizeof(fr->cid.data) * 2 + 1];
+
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " NEW_CONNECTION_ID(0x%02x) seq=%" PRIu64
+ " cid=0x%s retire_prior_to=%" PRIu64
+ " stateless_reset_token=0x%s"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq,
+ (const char *)ngtcp2_encode_hex(cid, fr->cid.data, fr->cid.datalen),
+ fr->retire_prior_to,
+ (const char *)ngtcp2_encode_hex(buf, fr->stateless_reset_token,
+ sizeof(fr->stateless_reset_token)));
+}
+
+static void log_fr_stop_sending(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_stop_sending *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " STOP_SENDING(0x%02x) id=0x%" PRIx64
+ " app_error_code=%s(0x%" PRIx64 ")"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ strapperrorcode(fr->app_error_code), fr->app_error_code);
+}
+
+static void log_fr_path_challenge(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_path_challenge *fr,
+ const char *dir) {
+ uint8_t buf[sizeof(fr->data) * 2 + 1];
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " PATH_CHALLENGE(0x%02x) data=0x%s"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data)));
+}
+
+static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_path_response *fr,
+ const char *dir) {
+ uint8_t buf[sizeof(fr->data) * 2 + 1];
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " PATH_RESPONSE(0x%02x) data=0x%s"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data)));
+}
+
+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);
+}
+
+static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_new_token *fr, const char *dir) {
+ /* Show at most first 64 bytes of token. If token is longer than 64
+ bytes, log first 64 bytes and then append "*" */
+ uint8_t buf[128 + 1 + 1];
+ uint8_t *p;
+
+ if (fr->token.len > 64) {
+ p = ngtcp2_encode_hex(buf, fr->token.base, 64);
+ p[128] = '*';
+ p[129] = '\0';
+ } else {
+ p = ngtcp2_encode_hex(buf, fr->token.base, fr->token.len);
+ }
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " NEW_TOKEN(0x%02x) token=0x%s len=%zu"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, (const char *)p, fr->token.len);
+}
+
+static void log_fr_retire_connection_id(ngtcp2_log *log,
+ const ngtcp2_pkt_hd *hd,
+ const ngtcp2_retire_connection_id *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " RETIRE_CONNECTION_ID(0x%02x) seq=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq);
+}
+
+static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_handshake_done *fr,
+ const char *dir) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02x)"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type);
+}
+
+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"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ ngtcp2_vec_len(fr->data, fr->datacnt));
+}
+
+static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr, const char *dir) {
+ switch (fr->type) {
+ case NGTCP2_FRAME_STREAM:
+ log_fr_stream(log, hd, &fr->stream, dir);
+ break;
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ log_fr_ack(log, hd, &fr->ack, dir);
+ break;
+ case NGTCP2_FRAME_PADDING:
+ log_fr_padding(log, hd, &fr->padding, dir);
+ break;
+ case NGTCP2_FRAME_RESET_STREAM:
+ log_fr_reset_stream(log, hd, &fr->reset_stream, dir);
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ log_fr_connection_close(log, hd, &fr->connection_close, dir);
+ break;
+ case NGTCP2_FRAME_MAX_DATA:
+ log_fr_max_data(log, hd, &fr->max_data, dir);
+ break;
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ log_fr_max_stream_data(log, hd, &fr->max_stream_data, dir);
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ log_fr_max_streams(log, hd, &fr->max_streams, dir);
+ break;
+ case NGTCP2_FRAME_PING:
+ log_fr_ping(log, hd, &fr->ping, dir);
+ break;
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ log_fr_data_blocked(log, hd, &fr->data_blocked, dir);
+ break;
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ log_fr_stream_data_blocked(log, hd, &fr->stream_data_blocked, dir);
+ break;
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ log_fr_streams_blocked(log, hd, &fr->streams_blocked, dir);
+ break;
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ log_fr_new_connection_id(log, hd, &fr->new_connection_id, dir);
+ break;
+ case NGTCP2_FRAME_STOP_SENDING:
+ log_fr_stop_sending(log, hd, &fr->stop_sending, dir);
+ break;
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ log_fr_path_challenge(log, hd, &fr->path_challenge, dir);
+ break;
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ log_fr_path_response(log, hd, &fr->path_response, dir);
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ log_fr_crypto(log, hd, &fr->crypto, dir);
+ break;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ log_fr_new_token(log, hd, &fr->new_token, dir);
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ log_fr_retire_connection_id(log, hd, &fr->retire_connection_id, dir);
+ break;
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ log_fr_handshake_done(log, hd, &fr->handshake_done, dir);
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ log_fr_datagram(log, hd, &fr->datagram, dir);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr) {
+ if (!log->log_printf) {
+ return;
+ }
+
+ log_fr(log, hd, fr, "rx");
+}
+
+void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr) {
+ if (!log->log_printf) {
+ return;
+ }
+
+ log_fr(log, hd, fr, "tx");
+}
+
+void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv) {
+ size_t i;
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ for (i = 0; i < nsv; ++i) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " v=0x%08x"),
+ NGTCP2_LOG_PKT_HD_FIELDS("rx"), sv[i]);
+ }
+}
+
+void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) {
+ uint8_t buf[sizeof(sr->stateless_reset_token) * 2 + 1];
+ ngtcp2_pkt_hd shd;
+ ngtcp2_pkt_hd *hd = &shd;
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ memset(&shd, 0, sizeof(shd));
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"),
+ NGTCP2_LOG_PKT_HD_FIELDS("rx"),
+ (const char *)ngtcp2_encode_hex(buf, sr->stateless_reset_token,
+ sizeof(sr->stateless_reset_token)),
+ sr->randlen);
+}
+
+void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
+ const ngtcp2_transport_params *params) {
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1];
+ uint8_t addr[16 * 2 + 7 + 1];
+ uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1];
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ if (params->stateless_reset_token_present) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " stateless_reset_token=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(
+ token, params->stateless_reset_token,
+ sizeof(params->stateless_reset_token)));
+ }
+
+ if (params->preferred_address_present) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.ipv4_addr=%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_ipv4(
+ addr, params->preferred_address.ipv4_addr));
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv4_port=%u"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv4_port);
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.ipv6_addr=%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_ipv6(
+ addr, params->preferred_address.ipv6_addr));
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv6_port=%u"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv6_port);
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.cid=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(
+ cid, params->preferred_address.cid.data,
+ params->preferred_address.cid.datalen));
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.stateless_reset_token=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(
+ token, params->preferred_address.stateless_reset_token,
+ sizeof(params->preferred_address.stateless_reset_token)));
+ }
+
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " original_destination_connection_id=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(cid, params->original_dcid.data,
+ params->original_dcid.datalen));
+
+ if (params->retry_scid_present) {
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_TP " retry_source_connection_id=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(cid, params->retry_scid.data,
+ params->retry_scid.datalen));
+ }
+ }
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_TP " initial_source_connection_id=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(cid, params->initial_scid.data,
+ params->initial_scid.datalen));
+
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_local);
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " initial_max_stream_data_bidi_remote=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_remote);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " initial_max_stream_data_uni=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " initial_max_streams_bidi=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " initial_max_streams_uni=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ params->max_idle_timeout / NGTCP2_MILLISECONDS);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " max_udp_payload_size=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->max_udp_payload_size);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " ack_delay_exponent=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ params->max_ack_delay / NGTCP2_MILLISECONDS);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->active_connection_id_limit);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " disable_active_migration=%d"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->disable_active_migration);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size);
+}
+
+void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
+ uint8_t flags, ngtcp2_tstamp sent_ts) {
+ if (!log->log_printf) {
+ 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);
+}
+
+static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const char *dir) {
+ uint8_t dcid[sizeof(hd->dcid.data) * 2 + 1];
+ uint8_t scid[sizeof(hd->scid.data) * 2 + 1];
+
+ if (!log->log_printf) {
+ 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);
+}
+
+void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
+ log_pkt_hd(log, hd, "rx");
+}
+
+void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
+ log_pkt_hd(log, hd, "tx");
+}
+
+void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
+ ...) {
+ va_list ap;
+ int n;
+ char buf[NGTCP2_LOG_BUFLEN];
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (n < 0 || (size_t)n >= sizeof(buf)) {
+ return;
+ }
+
+ log->log_printf(log->user_data, (NGTCP2_LOG_HD " %s"),
+ timestamp_cast(log->last_ts - log->ts), log->scid,
+ strevent(ev), buf);
+}
+
+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);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h
new file mode 100644
index 0000000000..bd1ac240a9
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h
@@ -0,0 +1,80 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_LOG_H
+#define NGTCP2_LOG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+
+typedef struct ngtcp2_log ngtcp2_log;
+
+struct ngtcp2_log {
+ /* log_printf is a sink to write log. NULL means no logging
+ output. */
+ ngtcp2_printf log_printf;
+ /* ts is the time point used to write time delta in the log. */
+ ngtcp2_tstamp ts;
+ /* last_ts is the most recent time point that this object is
+ told. */
+ ngtcp2_tstamp last_ts;
+ /* user_data is user-defined opaque data which is passed to
+ log_pritnf. */
+ void *user_data;
+ /* scid is SCID encoded as NULL-terminated hex string. */
+ uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1];
+};
+
+void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
+ ngtcp2_printf log_printf, ngtcp2_tstamp ts,
+ void *user_data);
+
+void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr);
+void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr);
+
+void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv);
+
+void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr);
+
+void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
+ const ngtcp2_transport_params *params);
+
+void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
+ uint8_t flags, ngtcp2_tstamp sent_ts);
+
+void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
+
+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);
+
+#endif /* NGTCP2_LOG_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h
new file mode 100644
index 0000000000..e2603aae15
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h
@@ -0,0 +1,53 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_MACRO_H
+#define NGTCP2_MACRO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stddef.h>
+
+#include <ngtcp2/ngtcp2.h>
+
+#define ngtcp2_min(A, B) ((A) < (B) ? (A) : (B))
+#define ngtcp2_max(A, B) ((A) > (B) ? (A) : (B))
+
+#define ngtcp2_struct_of(ptr, type, member) \
+ ((type *)(void *)((char *)(ptr)-offsetof(type, member)))
+
+/* ngtcp2_list_insert inserts |T| before |*PD|. The contract is that
+ this is singly linked list, and the next element is pointed by next
+ field of the previous element. |PD| must be a pointer to the
+ pointer to the next field of the previous element of |*PD|: if C is
+ the previous element of |PD|, PD = &C->next. */
+#define ngtcp2_list_insert(T, PD) \
+ do { \
+ (T)->next = *(PD); \
+ *(PD) = (T); \
+ } while (0)
+
+#endif /* NGTCP2_MACRO_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c
new file mode 100644
index 0000000000..5d24961dc9
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c
@@ -0,0 +1,332 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_map.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_conv.h"
+
+#define INITIAL_TABLE_LENGTH 256
+
+int 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->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 *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);
+ 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;
+ }
+ }
+}
+
+int ngtcp2_map_each(ngtcp2_map *map,
+ int (*func)(ngtcp2_map_entry *entry, void *ptr),
+ void *ptr) {
+ int rv;
+ 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) {
+ rv = func(bkt->ptr, ptr);
+ if (rv != 0) {
+ return rv;
+ }
+ assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0);
+ 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;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) {
+ entry->key = key;
+}
+
+/* FNV1a hash */
+static uint32_t hash(key_type key, uint32_t mod) {
+ uint8_t *p, *end;
+ uint32_t h = 0x811C9DC5u;
+
+ key = ngtcp2_htonl64(key);
+ p = (uint8_t *)&key;
+ end = p + sizeof(key_type);
+
+ for (; p != end;) {
+ h ^= *p++;
+ h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
+ }
+
+ return h & (mod - 1);
+}
+
+static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return *(key_type *)lhs < *(key_type *)rhs;
+}
+
+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;
+
+ if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) {
+ bkt->ptr = entry;
+ return 0;
+ }
+
+ if (!bkt->ksl) {
+ bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl));
+ if (bkt->ksl == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
+ }
+
+ if (bkt->ptr) {
+ rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
+ if (rv != 0) {
+ return rv;
+ }
+
+ bkt->ptr = NULL;
+ }
+
+ return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
+}
+
+/* new_tablelen must be power of 2 */
+static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) {
+ uint32_t i;
+ ngtcp2_map_bucket *new_table;
+ ngtcp2_map_bucket *bkt;
+ ngtcp2_ksl_it it;
+ int rv;
+
+ new_table =
+ ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket));
+ if (new_table == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ 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);
+ continue;
+ }
+
+ 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);
+ }
+ }
+
+ ngtcp2_mem_free(map->mem, map->table);
+ map->tablelen = new_tablelen;
+ 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 rv;
+
+ /* 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;
+ }
+ }
+ rv = map_insert(map, map->table, map->tablelen, new_entry);
+ if (rv != 0) {
+ return rv;
+ }
+ ++map->size;
+ 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;
+
+ if (bkt->ptr) {
+ if (bkt->ptr->key == key) {
+ return bkt->ptr;
+ }
+ 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) {
+ return NULL;
+ }
+ return ngtcp2_ksl_it_get(&it);
+ }
+
+ return NULL;
+}
+
+int ngtcp2_map_remove(ngtcp2_map *map, key_type key) {
+ ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
+ int rv;
+
+ if (bkt->ptr) {
+ if (bkt->ptr->key == key) {
+ bkt->ptr = NULL;
+ --map->size;
+ return 0;
+ }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (bkt->ksl) {
+ rv = ngtcp2_ksl_remove(bkt->ksl, NULL, &key);
+ if (rv != 0) {
+ return rv;
+ }
+ --map->size;
+ return 0;
+ }
+
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+}
+
+void ngtcp2_map_clear(ngtcp2_map *map) {
+ uint32_t i;
+ ngtcp2_map_bucket *bkt;
+
+ 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;
+ }
+ }
+
+ map->size = 0;
+}
+
+size_t ngtcp2_map_size(ngtcp2_map *map) { return map->size; }
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h
new file mode 100644
index 0000000000..bbb2e705d6
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h
@@ -0,0 +1,152 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_MAP_H
+#define NGTCP2_MAP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#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 struct ngtcp2_map_bucket {
+ ngtcp2_map_entry *ptr;
+ ngtcp2_ksl *ksl;
+} ngtcp2_map_bucket;
+
+typedef struct ngtcp2_map {
+ ngtcp2_map_bucket *table;
+ const ngtcp2_mem *mem;
+ size_t size;
+ uint32_t tablelen;
+} 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);
+
+/*
+ * Deallocates any resources allocated for |map|. The stored entries
+ * are not freed by this function. Use ngtcp2_map_each_free() to free
+ * each entries.
+ */
+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
+ * 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 *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|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * The item associated by |key| already exists.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry);
+
+/*
+ * Returns the entry associated by the key |key|. If there is no such
+ * entry, this function returns NULL.
+ */
+ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key);
+
+/*
+ * Removes the entry associated by the key |key| from the |map|. The
+ * removed entry 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.
+ */
+int ngtcp2_map_remove(ngtcp2_map *map, key_type key);
+
+/*
+ * Removes all entries from |map|.
+ */
+void ngtcp2_map_clear(ngtcp2_map *map);
+
+/*
+ * Returns the number of items stored in the map |map|.
+ */
+size_t ngtcp2_map_size(ngtcp2_map *map);
+
+/*
+ * Applies the function |func| to each entry 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
+ * |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
+ * ngtcp2_map_each_free() instead.
+ */
+int ngtcp2_map_each(ngtcp2_map *map,
+ int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr);
+
+#endif /* NGTCP2_MAP_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c
new file mode 100644
index 0000000000..2c036ad163
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c
@@ -0,0 +1,75 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_mem.h"
+
+static void *default_malloc(size_t size, void *mem_user_data) {
+ (void)mem_user_data;
+
+ return malloc(size);
+}
+
+static void default_free(void *ptr, void *mem_user_data) {
+ (void)mem_user_data;
+
+ free(ptr);
+}
+
+static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
+ (void)mem_user_data;
+
+ return calloc(nmemb, size);
+}
+
+static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
+ (void)mem_user_data;
+
+ return realloc(ptr, size);
+}
+
+static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free,
+ default_calloc, default_realloc};
+
+const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; }
+
+void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) {
+ return mem->malloc(size, mem->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);
+}
+
+void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) {
+ return mem->calloc(nmemb, size, mem->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);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h
new file mode 100644
index 0000000000..cdecf8763a
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h
@@ -0,0 +1,43 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_MEM_H
+#define NGTCP2_MEM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/* Convenient wrapper functions to call allocator function in
+ |mem|. */
+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);
+
+#endif /* NGTCP2_MEM_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c
new file mode 100644
index 0000000000..3f35f28ef6
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c
@@ -0,0 +1,74 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_path.h"
+
+#include <string.h>
+
+#include "ngtcp2_addr.h"
+
+void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
+ const ngtcp2_addr *remote) {
+ path->local = *local;
+ path->remote = *remote;
+}
+
+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);
+}
+
+int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) {
+ return ngtcp2_addr_eq(&a->local, &b->local) &&
+ ngtcp2_addr_eq(&a->remote, &b->remote);
+}
+
+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);
+ ngtcp2_addr_init(&ps->path.remote,
+ (const struct sockaddr *)&ps->remote_addrbuf, 0,
+ remote_user_data);
+
+ ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen);
+ ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen);
+}
+
+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);
+}
+
+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.remote,
+ (const struct sockaddr *)&ps->remote_addrbuf, 0, NULL);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h
new file mode 100644
index 0000000000..0c360e9362
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h
@@ -0,0 +1,49 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PATH_H
+#define NGTCP2_PATH_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_path_init initializes |path| with the given addresses. Note
+ * that the buffer pointed by local->addr and remote->addr are not
+ * copied. Their pointer values are assigned instead.
+ */
+void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
+ const ngtcp2_addr *remote);
+
+/*
+ * ngtcp2_path_storage_init2 initializes |ps| using |path| as initial
+ * data.
+ */
+void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps,
+ const ngtcp2_path *path);
+
+#endif /* NGTCP2_PATH_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c
new file mode 100644
index 0000000000..ddfbd24bbc
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c
@@ -0,0 +1,2432 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pkt.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "ngtcp2_conv.h"
+#include "ngtcp2_str.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_vec.h"
+
+int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen, ngtcp2_tstamp ts,
+ const ngtcp2_mem *mem) {
+ *ppc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pkt_chain) + pktlen);
+ if (*ppc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_path_storage_init2(&(*ppc)->path, path);
+ (*ppc)->pi = *pi;
+ (*ppc)->next = NULL;
+ (*ppc)->pkt = (uint8_t *)(*ppc) + sizeof(ngtcp2_pkt_chain);
+ (*ppc)->pktlen = pktlen;
+ (*ppc)->dgramlen = dgramlen;
+ (*ppc)->ts = ts;
+
+ memcpy((*ppc)->pkt, pkt, pktlen);
+
+ return 0;
+}
+
+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,
+ size_t datalen, size_t short_dcidlen) {
+ size_t len;
+ uint32_t version;
+ size_t dcidlen, scidlen;
+
+ assert(datalen);
+
+ if (data[0] & NGTCP2_HEADER_FORM_BIT) {
+ /* 1 byte (Header Form, Fixed Bit, Long Packet Type, Type-Specific bits)
+ * 4 bytes Version
+ * 1 byte DCID Length
+ * 1 byte SCID Length
+ */
+ len = 1 + 4 + 1 + 1;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dcidlen = data[5];
+ len += dcidlen;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ scidlen = data[5 + 1 + dcidlen];
+ len += scidlen;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ 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)) &&
+ (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 && version != NGTCP2_PROTO_VER_V1 &&
+ (version < NGTCP2_PROTO_VER_DRAFT_MIN ||
+ NGTCP2_PROTO_VER_DRAFT_MAX < version)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ assert(short_dcidlen <= NGTCP2_MAX_CIDLEN);
+
+ len = 1 + short_dcidlen;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ *pversion = 0;
+ *pdcid = &data[1];
+ *pdcidlen = short_dcidlen;
+ *pscid = NULL;
+ *pscidlen = 0;
+
+ return 0;
+}
+
+void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type,
+ const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ int64_t pkt_num, size_t pkt_numlen, uint32_t version,
+ size_t len) {
+ hd->flags = flags;
+ hd->type = type;
+ if (dcid) {
+ hd->dcid = *dcid;
+ } else {
+ ngtcp2_cid_zero(&hd->dcid);
+ }
+ if (scid) {
+ hd->scid = *scid;
+ } else {
+ ngtcp2_cid_zero(&hd->scid);
+ }
+ hd->pkt_num = pkt_num;
+ hd->token.base = NULL;
+ hd->token.len = 0;
+ hd->pkt_numlen = pkt_numlen;
+ hd->version = version;
+ hd->len = len;
+}
+
+static int has_mask(uint8_t b, uint8_t mask) { return (b & mask) == mask; }
+
+ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen) {
+ uint8_t type;
+ uint32_t version;
+ size_t dcil, scil;
+ const uint8_t *p;
+ size_t len = 0;
+ size_t n;
+ size_t ntokenlen = 0;
+ const uint8_t *token = NULL;
+ size_t tokenlen = 0;
+ uint64_t vi;
+
+ if (pktlen < 5) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ version = ngtcp2_get_uint32(&pkt[1]);
+
+ if (version == 0) {
+ type = NGTCP2_PKT_VERSION_NEGOTIATION;
+ /* 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;
+ }
+
+ type = ngtcp2_pkt_get_type_long(pkt[0]);
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN -
+ 1; /* Cut packet number field */
+ break;
+ case NGTCP2_PKT_RETRY:
+ /* Retry packet does not have packet number and length fields */
+ len = 5 + 2;
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ case NGTCP2_PKT_0RTT:
+ len = NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */
+ break;
+ default:
+ /* Unreachable */
+ assert(0);
+ }
+ }
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = &pkt[5];
+ dcil = *p;
+ if (dcil > NGTCP2_MAX_CIDLEN) {
+ /* QUIC v1 implementation never expect to receive CID length more
+ than NGTCP2_MAX_CIDLEN. */
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ len += dcil;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p += 1 + dcil;
+ scil = *p;
+ if (scil > NGTCP2_MAX_CIDLEN) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ len += scil;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p += 1 + scil;
+
+ if (type == NGTCP2_PKT_INITIAL) {
+ /* Token Length */
+ ntokenlen = ngtcp2_get_varint_len(p);
+ len += ntokenlen - 1;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ vi = ngtcp2_get_varint(&ntokenlen, p);
+ if (pktlen - len < vi) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ tokenlen = (size_t)vi;
+ len += tokenlen;
+
+ p += ntokenlen;
+
+ if (tokenlen) {
+ token = p;
+ }
+
+ p += tokenlen;
+ }
+
+ switch (type) {
+ case NGTCP2_PKT_VERSION_NEGOTIATION:
+ case NGTCP2_PKT_RETRY:
+ break;
+ default:
+ /* Length */
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ }
+
+ dest->flags = NGTCP2_PKT_FLAG_LONG_FORM;
+ dest->type = type;
+ dest->version = version;
+ dest->pkt_num = 0;
+ dest->pkt_numlen = 0;
+
+ p = &pkt[6];
+ ngtcp2_cid_init(&dest->dcid, p, dcil);
+ p += dcil + 1;
+ ngtcp2_cid_init(&dest->scid, p, scil);
+ p += scil;
+
+ dest->token.base = (uint8_t *)token;
+ dest->token.len = tokenlen;
+ p += ntokenlen + tokenlen;
+
+ switch (type) {
+ case NGTCP2_PKT_VERSION_NEGOTIATION:
+ case NGTCP2_PKT_RETRY:
+ dest->len = 0;
+ break;
+ default:
+ vi = ngtcp2_get_varint(&n, p);
+ if (vi > SIZE_MAX) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ dest->len = (size_t)vi;
+ p += n;
+ }
+
+ assert((size_t)(p - pkt) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+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;
+
+ assert(dcidlen <= NGTCP2_MAX_CIDLEN);
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) ||
+ (pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = &pkt[1];
+
+ dest->type = NGTCP2_PKT_SHORT;
+
+ ngtcp2_cid_init(&dest->dcid, p, dcidlen);
+ p += dcidlen;
+
+ /* Set 0 to SCID so that we don't accidentally reference it and gets
+ garbage. */
+ ngtcp2_cid_zero(&dest->scid);
+
+ dest->flags = NGTCP2_PKT_FLAG_NONE;
+ dest->version = 0;
+ dest->len = 0;
+ dest->pkt_num = 0;
+ dest->pkt_numlen = 0;
+
+ assert((size_t)(p - pkt) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd) {
+ uint8_t *p;
+ size_t len = NGTCP2_MIN_LONG_HEADERLEN + hd->dcid.datalen + hd->scid.datalen -
+ 2; /* NGTCP2_MIN_LONG_HEADERLEN includes 1 byte for
+ len and 1 byte for packet number. */
+
+ if (hd->type != NGTCP2_PKT_RETRY) {
+ len += 2 /* Length */ + hd->pkt_numlen;
+ }
+
+ if (hd->type == NGTCP2_PKT_INITIAL) {
+ len += ngtcp2_put_varint_len(hd->token.len) + hd->token.len;
+ }
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = (uint8_t)(NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK |
+ (hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1));
+ p = ngtcp2_put_uint32be(p, hd->version);
+ *p++ = (uint8_t)hd->dcid.datalen;
+ if (hd->dcid.datalen) {
+ p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen);
+ }
+ *p++ = (uint8_t)hd->scid.datalen;
+ if (hd->scid.datalen) {
+ p = ngtcp2_cpymem(p, hd->scid.data, hd->scid.datalen);
+ }
+
+ if (hd->type == NGTCP2_PKT_INITIAL) {
+ p = ngtcp2_put_varint(p, hd->token.len);
+ if (hd->token.len) {
+ p = ngtcp2_cpymem(p, hd->token.base, hd->token.len);
+ }
+ }
+
+ if (hd->type != NGTCP2_PKT_RETRY) {
+ p = ngtcp2_put_varint14(p, (uint16_t)hd->len);
+ p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd) {
+ uint8_t *p;
+ size_t len = 1 + hd->dcid.datalen + hd->pkt_numlen;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1);
+ if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) {
+ *p |= NGTCP2_SHORT_KEY_PHASE_BIT;
+ }
+
+ ++p;
+
+ if (hd->dcid.datalen) {
+ p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen);
+ }
+
+ p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
+ size_t payloadlen) {
+ uint8_t type;
+
+ if (payloadlen == 0) {
+ return 0;
+ }
+
+ type = payload[0];
+
+ switch (type) {
+ case NGTCP2_FRAME_PADDING:
+ return (ngtcp2_ssize)ngtcp2_pkt_decode_padding_frame(&dest->padding,
+ payload, payloadlen);
+ case NGTCP2_FRAME_RESET_STREAM:
+ return ngtcp2_pkt_decode_reset_stream_frame(&dest->reset_stream, payload,
+ payloadlen);
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ return ngtcp2_pkt_decode_connection_close_frame(&dest->connection_close,
+ payload, payloadlen);
+ case NGTCP2_FRAME_MAX_DATA:
+ return ngtcp2_pkt_decode_max_data_frame(&dest->max_data, payload,
+ payloadlen);
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ return ngtcp2_pkt_decode_max_stream_data_frame(&dest->max_stream_data,
+ payload, payloadlen);
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ return ngtcp2_pkt_decode_max_streams_frame(&dest->max_streams, payload,
+ payloadlen);
+ case NGTCP2_FRAME_PING:
+ return ngtcp2_pkt_decode_ping_frame(&dest->ping, payload, payloadlen);
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ return ngtcp2_pkt_decode_data_blocked_frame(&dest->data_blocked, payload,
+ payloadlen);
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ return ngtcp2_pkt_decode_stream_data_blocked_frame(
+ &dest->stream_data_blocked, payload, payloadlen);
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ return ngtcp2_pkt_decode_streams_blocked_frame(&dest->streams_blocked,
+ payload, payloadlen);
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ return ngtcp2_pkt_decode_new_connection_id_frame(&dest->new_connection_id,
+ payload, payloadlen);
+ case NGTCP2_FRAME_STOP_SENDING:
+ return ngtcp2_pkt_decode_stop_sending_frame(&dest->stop_sending, payload,
+ payloadlen);
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ return ngtcp2_pkt_decode_ack_frame(&dest->ack, payload, payloadlen);
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ return ngtcp2_pkt_decode_path_challenge_frame(&dest->path_challenge,
+ payload, payloadlen);
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ return ngtcp2_pkt_decode_path_response_frame(&dest->path_response, payload,
+ payloadlen);
+ case NGTCP2_FRAME_CRYPTO:
+ return ngtcp2_pkt_decode_crypto_frame(&dest->crypto, payload, payloadlen);
+ case NGTCP2_FRAME_NEW_TOKEN:
+ return ngtcp2_pkt_decode_new_token_frame(&dest->new_token, payload,
+ payloadlen);
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ return ngtcp2_pkt_decode_retire_connection_id_frame(
+ &dest->retire_connection_id, payload, payloadlen);
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ return ngtcp2_pkt_decode_handshake_done_frame(&dest->handshake_done,
+ payload, payloadlen);
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ return ngtcp2_pkt_decode_datagram_frame(&dest->datagram, payload,
+ payloadlen);
+ default:
+ if (has_mask(type, NGTCP2_FRAME_STREAM)) {
+ return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen);
+ }
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ uint8_t type;
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t datalen;
+ size_t ndatalen = 0;
+ size_t n;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ if (type & NGTCP2_STREAM_OFF_BIT) {
+ ++len;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+
+ if (type & NGTCP2_STREAM_LEN_BIT) {
+ ++len;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ ndatalen = ngtcp2_get_varint_len(p);
+ len += ndatalen - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ vi = ngtcp2_get_varint(&ndatalen, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ datalen = (size_t)vi;
+ len += datalen;
+ } else {
+ len = payloadlen;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_STREAM;
+ dest->flags = (uint8_t)(type & ~NGTCP2_FRAME_STREAM);
+ dest->fin = (type & NGTCP2_STREAM_FIN_BIT) != 0;
+ dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p);
+ p += n;
+
+ if (type & NGTCP2_STREAM_OFF_BIT) {
+ dest->offset = ngtcp2_get_varint(&n, p);
+ p += n;
+ } else {
+ dest->offset = 0;
+ }
+
+ if (type & NGTCP2_STREAM_LEN_BIT) {
+ p += ndatalen;
+ } else {
+ datalen = payloadlen - (size_t)(p - payload);
+ }
+
+ if (datalen) {
+ dest->data[0].len = datalen;
+ dest->data[0].base = (uint8_t *)p;
+ dest->datacnt = 1;
+ p += datalen;
+ } else {
+ dest->datacnt = 0;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t num_blks, max_num_blks;
+ size_t nnum_blks;
+ size_t len = 1 + 1 + 1 + 1 + 1;
+ const uint8_t *p;
+ size_t i, j;
+ ngtcp2_ack_blk *blk;
+ size_t n;
+ uint8_t type;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ /* Largest Acknowledged */
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ /* ACK Delay */
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ /* ACK Block Count */
+ nnum_blks = ngtcp2_get_varint_len(p);
+ len += nnum_blks - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ vi = ngtcp2_get_varint(&nnum_blks, p);
+ if (vi > SIZE_MAX / (1 + 1) || payloadlen - len < vi * (1 + 1)) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ num_blks = (size_t)vi;
+ len += num_blks * (1 + 1);
+
+ p += nnum_blks;
+
+ /* First ACK Block */
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ for (i = 0; i < num_blks; ++i) {
+ /* Gap, and Additional ACK Block */
+ for (j = 0; j < 2; ++j) {
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+ }
+
+ if (type == NGTCP2_FRAME_ACK_ECN) {
+ len += 3;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ for (i = 0; i < 3; ++i) {
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+ }
+
+ /* TODO We might not decode all blocks. It could be very large. */
+ max_num_blks = ngtcp2_min(NGTCP2_MAX_ACK_BLKS, num_blks);
+
+ p = payload + 1;
+
+ dest->type = type;
+ dest->largest_ack = (int64_t)ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->ack_delay = ngtcp2_get_varint(&n, p);
+ /* This value will be assigned in the upper layer. */
+ dest->ack_delay_unscaled = 0;
+ p += n;
+ dest->num_blks = max_num_blks;
+ p += nnum_blks;
+ dest->first_ack_blklen = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ for (i = 0; i < max_num_blks; ++i) {
+ blk = &dest->blks[i];
+ blk->gap = ngtcp2_get_varint(&n, p);
+ p += n;
+ blk->blklen = ngtcp2_get_varint(&n, p);
+ p += n;
+ }
+ for (i = max_num_blks; i < num_blks; ++i) {
+ p += ngtcp2_get_varint_len(p);
+ p += ngtcp2_get_varint_len(p);
+ }
+
+ if (type == NGTCP2_FRAME_ACK_ECN) {
+ dest->ecn.ect0 = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ dest->ecn.ect1 = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ dest->ecn.ce = ngtcp2_get_varint(&n, p);
+ p += n;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ const uint8_t *p, *ep;
+
+ assert(payloadlen > 0);
+
+ p = payload + 1;
+ ep = payload + payloadlen;
+
+ for (; p != ep && *p == NGTCP2_FRAME_PADDING; ++p)
+ ;
+
+ dest->type = NGTCP2_FRAME_PADDING;
+ dest->len = (size_t)(p - payload);
+
+ return dest->len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ p += n;
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ p += n;
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_RESET_STREAM;
+ dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->app_error_code = ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->final_size = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame(
+ ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t reasonlen;
+ size_t nreasonlen;
+ size_t n;
+ uint8_t type;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ if (type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ ++len;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+
+ nreasonlen = ngtcp2_get_varint_len(p);
+ len += nreasonlen - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ vi = ngtcp2_get_varint(&nreasonlen, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ reasonlen = (size_t)vi;
+ len += reasonlen;
+
+ p = payload + 1;
+
+ dest->type = type;
+ dest->error_code = ngtcp2_get_varint(&n, p);
+ p += n;
+ if (type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ dest->frame_type = ngtcp2_get_varint(&n, p);
+ p += n;
+ } else {
+ dest->frame_type = 0;
+ }
+ dest->reasonlen = reasonlen;
+ p += nreasonlen;
+ if (reasonlen == 0) {
+ dest->reason = NULL;
+ } else {
+ dest->reason = (uint8_t *)p;
+ p += reasonlen;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = NGTCP2_FRAME_MAX_DATA;
+ dest->max_data = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame(
+ ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->max_stream_data = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = payload[0];
+ dest->max_streams = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ (void)payload;
+ (void)payloadlen;
+
+ dest->type = NGTCP2_FRAME_PING;
+ return 1;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = NGTCP2_FRAME_DATA_BLOCKED;
+ dest->offset = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_STREAM_DATA_BLOCKED;
+ dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->offset = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame(
+ ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = payload[0];
+ dest->max_streams = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame(
+ ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1 + 1 + 1 + 16;
+ const uint8_t *p;
+ size_t n;
+ size_t cil;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ cil = *p;
+ if (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ len += cil;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ dest->seq = ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->retire_prior_to = ngtcp2_get_varint(&n, p);
+ p += n + 1;
+ ngtcp2_cid_init(&dest->cid, p, cil);
+ p += cil;
+ memcpy(dest->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN);
+ p += NGTCP2_STATELESS_RESET_TOKENLEN;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ p += n;
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_STOP_SENDING;
+ dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->app_error_code = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 8;
+ const uint8_t *p;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_PATH_CHALLENGE;
+ ngtcp2_cpymem(dest->data, p, sizeof(dest->data));
+ p += sizeof(dest->data);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 8;
+ const uint8_t *p;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_PATH_RESPONSE;
+ ngtcp2_cpymem(dest->data, p, sizeof(dest->data));
+ p += sizeof(dest->data);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t datalen;
+ size_t ndatalen;
+ size_t n;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ ndatalen = ngtcp2_get_varint_len(p);
+ len += ndatalen - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ vi = ngtcp2_get_varint(&ndatalen, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ datalen = (size_t)vi;
+ len += datalen;
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_CRYPTO;
+ dest->offset = ngtcp2_get_varint(&n, p);
+ p += n;
+ dest->data[0].len = datalen;
+ p += ndatalen;
+ if (dest->data[0].len) {
+ dest->data[0].base = (uint8_t *)p;
+ p += dest->data[0].len;
+ dest->datacnt = 1;
+ } else {
+ dest->data[0].base = NULL;
+ dest->datacnt = 0;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+ size_t datalen;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ vi = ngtcp2_get_varint(&n, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ datalen = (size_t)vi;
+ len += datalen;
+
+ dest->type = NGTCP2_FRAME_NEW_TOKEN;
+ dest->token.len = datalen;
+ p += n;
+ dest->token.base = (uint8_t *)p;
+ p += dest->token.len;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+ dest->seq = ngtcp2_get_varint(&n, p);
+ p += n;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ (void)payload;
+ (void)payloadlen;
+
+ dest->type = NGTCP2_FRAME_HANDSHAKE_DONE;
+ return 1;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1;
+ const uint8_t *p;
+ uint8_t type;
+ size_t datalen;
+ size_t n;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ switch (type) {
+ case NGTCP2_FRAME_DATAGRAM:
+ datalen = payloadlen - 1;
+ len = payloadlen;
+ break;
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ ++len;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ n = ngtcp2_get_varint_len(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ vi = ngtcp2_get_varint(&n, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_FRAME_ENCODING_ERROR;
+ }
+
+ datalen = (size_t)vi;
+ len += datalen;
+ break;
+ default:
+ assert(0);
+ }
+
+ dest->type = type;
+
+ if (datalen == 0) {
+ dest->datacnt = 0;
+ dest->data = NULL;
+
+ if (type == NGTCP2_FRAME_DATAGRAM_LEN) {
+ p += n;
+ }
+ } else {
+ dest->datacnt = 1;
+ dest->data = dest->rdata;
+ dest->rdata[0].len = datalen;
+
+ if (type == NGTCP2_FRAME_DATAGRAM_LEN) {
+ p += n;
+ }
+
+ dest->rdata[0].base = (uint8_t *)p;
+ p += datalen;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
+ ngtcp2_frame *fr) {
+ switch (fr->type) {
+ case NGTCP2_FRAME_STREAM:
+ return ngtcp2_pkt_encode_stream_frame(out, outlen, &fr->stream);
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ return ngtcp2_pkt_encode_ack_frame(out, outlen, &fr->ack);
+ case NGTCP2_FRAME_PADDING:
+ return ngtcp2_pkt_encode_padding_frame(out, outlen, &fr->padding);
+ case NGTCP2_FRAME_RESET_STREAM:
+ return ngtcp2_pkt_encode_reset_stream_frame(out, outlen, &fr->reset_stream);
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ return ngtcp2_pkt_encode_connection_close_frame(out, outlen,
+ &fr->connection_close);
+ case NGTCP2_FRAME_MAX_DATA:
+ return ngtcp2_pkt_encode_max_data_frame(out, outlen, &fr->max_data);
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ return ngtcp2_pkt_encode_max_stream_data_frame(out, outlen,
+ &fr->max_stream_data);
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ return ngtcp2_pkt_encode_max_streams_frame(out, outlen, &fr->max_streams);
+ case NGTCP2_FRAME_PING:
+ return ngtcp2_pkt_encode_ping_frame(out, outlen, &fr->ping);
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ return ngtcp2_pkt_encode_data_blocked_frame(out, outlen, &fr->data_blocked);
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ return ngtcp2_pkt_encode_stream_data_blocked_frame(
+ out, outlen, &fr->stream_data_blocked);
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ return ngtcp2_pkt_encode_streams_blocked_frame(out, outlen,
+ &fr->streams_blocked);
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ return ngtcp2_pkt_encode_new_connection_id_frame(out, outlen,
+ &fr->new_connection_id);
+ case NGTCP2_FRAME_STOP_SENDING:
+ return ngtcp2_pkt_encode_stop_sending_frame(out, outlen, &fr->stop_sending);
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ return ngtcp2_pkt_encode_path_challenge_frame(out, outlen,
+ &fr->path_challenge);
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ return ngtcp2_pkt_encode_path_response_frame(out, outlen,
+ &fr->path_response);
+ case NGTCP2_FRAME_CRYPTO:
+ return ngtcp2_pkt_encode_crypto_frame(out, outlen, &fr->crypto);
+ case NGTCP2_FRAME_NEW_TOKEN:
+ return ngtcp2_pkt_encode_new_token_frame(out, outlen, &fr->new_token);
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ return ngtcp2_pkt_encode_retire_connection_id_frame(
+ out, outlen, &fr->retire_connection_id);
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ return ngtcp2_pkt_encode_handshake_done_frame(out, outlen,
+ &fr->handshake_done);
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ return ngtcp2_pkt_encode_datagram_frame(out, outlen, &fr->datagram);
+ default:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen,
+ ngtcp2_stream *fr) {
+ size_t len = 1;
+ uint8_t flags = NGTCP2_STREAM_LEN_BIT;
+ uint8_t *p;
+ size_t i;
+ size_t datalen = 0;
+
+ if (fr->fin) {
+ flags |= NGTCP2_STREAM_FIN_BIT;
+ }
+
+ if (fr->offset) {
+ flags |= NGTCP2_STREAM_OFF_BIT;
+ len += ngtcp2_put_varint_len(fr->offset);
+ }
+
+ len += ngtcp2_put_varint_len((uint64_t)fr->stream_id);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ datalen += fr->data[i].len;
+ }
+
+ len += ngtcp2_put_varint_len(datalen);
+ len += datalen;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = flags | NGTCP2_FRAME_STREAM;
+
+ fr->flags = flags;
+
+ p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id);
+
+ if (fr->offset) {
+ p = ngtcp2_put_varint(p, fr->offset);
+ }
+
+ p = ngtcp2_put_varint(p, datalen);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ assert(fr->data[i].len);
+ assert(fr->data[i].base);
+ p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen,
+ ngtcp2_ack *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->largest_ack) +
+ ngtcp2_put_varint_len(fr->ack_delay) +
+ ngtcp2_put_varint_len(fr->num_blks) +
+ ngtcp2_put_varint_len(fr->first_ack_blklen);
+ uint8_t *p;
+ size_t i;
+ const ngtcp2_ack_blk *blk;
+
+ for (i = 0; i < fr->num_blks; ++i) {
+ blk = &fr->blks[i];
+ len += ngtcp2_put_varint_len(blk->gap);
+ len += ngtcp2_put_varint_len(blk->blklen);
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ len += ngtcp2_put_varint_len(fr->ecn.ect0) +
+ ngtcp2_put_varint_len(fr->ecn.ect1) +
+ ngtcp2_put_varint_len(fr->ecn.ce);
+ }
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_varint(p, (uint64_t)fr->largest_ack);
+ p = ngtcp2_put_varint(p, fr->ack_delay);
+ p = ngtcp2_put_varint(p, fr->num_blks);
+ p = ngtcp2_put_varint(p, fr->first_ack_blklen);
+
+ for (i = 0; i < fr->num_blks; ++i) {
+ blk = &fr->blks[i];
+ p = ngtcp2_put_varint(p, blk->gap);
+ p = ngtcp2_put_varint(p, blk->blklen);
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ p = ngtcp2_put_varint(p, fr->ecn.ect0);
+ p = ngtcp2_put_varint(p, fr->ecn.ect1);
+ p = ngtcp2_put_varint(p, fr->ecn.ce);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_padding *fr) {
+ if (outlen < fr->len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ memset(out, 0, fr->len);
+
+ return (ngtcp2_ssize)fr->len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_reset_stream *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) +
+ ngtcp2_put_varint_len(fr->app_error_code) +
+ ngtcp2_put_varint_len(fr->final_size);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_RESET_STREAM;
+ p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_varint(p, fr->app_error_code);
+ p = ngtcp2_put_varint(p, fr->final_size);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_connection_close *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->error_code) +
+ (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE
+ ? ngtcp2_put_varint_len(fr->frame_type)
+ : 0) +
+ ngtcp2_put_varint_len(fr->reasonlen) + fr->reasonlen;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_varint(p, fr->error_code);
+ if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ p = ngtcp2_put_varint(p, fr->frame_type);
+ }
+ p = ngtcp2_put_varint(p, fr->reasonlen);
+ if (fr->reasonlen) {
+ p = ngtcp2_cpymem(p, fr->reason, fr->reasonlen);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_data *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->max_data);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_MAX_DATA;
+ p = ngtcp2_put_varint(p, fr->max_data);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_stream_data *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) +
+ ngtcp2_put_varint_len(fr->max_stream_data);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_MAX_STREAM_DATA;
+ p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_varint(p, fr->max_stream_data);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_streams *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->max_streams);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_varint(p, fr->max_streams);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_ping *fr) {
+ (void)fr;
+
+ if (outlen < 1) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *out++ = NGTCP2_FRAME_PING;
+
+ return 1;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_data_blocked *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->offset);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_DATA_BLOCKED;
+ p = ngtcp2_put_varint(p, fr->offset);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) +
+ ngtcp2_put_varint_len(fr->offset);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_STREAM_DATA_BLOCKED;
+ p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_varint(p, fr->offset);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_streams_blocked *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->max_streams);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_varint(p, fr->max_streams);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_connection_id *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->seq) +
+ ngtcp2_put_varint_len(fr->retire_prior_to) + 1 +
+ fr->cid.datalen + NGTCP2_STATELESS_RESET_TOKENLEN;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ p = ngtcp2_put_varint(p, fr->seq);
+ p = ngtcp2_put_varint(p, fr->retire_prior_to);
+ *p++ = (uint8_t)fr->cid.datalen;
+ p = ngtcp2_cpymem(p, fr->cid.data, fr->cid.datalen);
+ p = ngtcp2_cpymem(p, fr->stateless_reset_token,
+ NGTCP2_STATELESS_RESET_TOKENLEN);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_stop_sending *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) +
+ ngtcp2_put_varint_len(fr->app_error_code);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_STOP_SENDING;
+ p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_varint(p, fr->app_error_code);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_challenge *fr) {
+ size_t len = 1 + 8;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_PATH_CHALLENGE;
+ p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data));
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_response *fr) {
+ size_t len = 1 + 8;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_PATH_RESPONSE;
+ p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data));
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_crypto *fr) {
+ size_t len = 1;
+ uint8_t *p;
+ size_t i;
+ size_t datalen = 0;
+
+ len += ngtcp2_put_varint_len(fr->offset);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ datalen += fr->data[i].len;
+ }
+
+ len += ngtcp2_put_varint_len(datalen);
+ len += datalen;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_CRYPTO;
+
+ p = ngtcp2_put_varint(p, fr->offset);
+ p = ngtcp2_put_varint(p, datalen);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ assert(fr->data[i].base);
+ p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_token *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->token.len) + fr->token.len;
+ uint8_t *p;
+
+ assert(fr->token.len);
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_NEW_TOKEN;
+
+ p = ngtcp2_put_varint(p, fr->token.len);
+ p = ngtcp2_cpymem(p, fr->token.base, fr->token.len);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr) {
+ size_t len = 1 + ngtcp2_put_varint_len(fr->seq);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+
+ p = ngtcp2_put_varint(p, fr->seq);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_handshake_done *fr) {
+ (void)fr;
+
+ if (outlen < 1) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *out++ = NGTCP2_FRAME_HANDSHAKE_DONE;
+
+ return 1;
+}
+
+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 =
+ 1 +
+ (fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_varint_len(datalen)) +
+ datalen;
+ uint8_t *p;
+ size_t i;
+
+ assert(fr->type == NGTCP2_FRAME_DATAGRAM ||
+ fr->type == NGTCP2_FRAME_DATAGRAM_LEN);
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) {
+ p = ngtcp2_put_varint(p, (uint64_t)datalen);
+ }
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ assert(fr->data[i].len);
+ assert(fr->data[i].base);
+ p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_write_version_negotiation(
+ uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid,
+ size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv,
+ size_t nsv) {
+ size_t len = 1 + 4 + 1 + dcidlen + 1 + scidlen + nsv * 4;
+ uint8_t *p;
+ size_t i;
+
+ assert(dcidlen < 256);
+ assert(scidlen < 256);
+
+ if (destlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = dest;
+
+ *p++ = 0x80 | unused_random;
+ p = ngtcp2_put_uint32be(p, 0);
+ *p++ = (uint8_t)dcidlen;
+ if (dcidlen) {
+ p = ngtcp2_cpymem(p, dcid, dcidlen);
+ }
+ *p++ = (uint8_t)scidlen;
+ if (scidlen) {
+ p = ngtcp2_cpymem(p, scid, scidlen);
+ }
+
+ for (i = 0; i < nsv; ++i) {
+ p = ngtcp2_put_uint32be(p, sv[i]);
+ }
+
+ assert((size_t)(p - dest) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ const uint8_t *end = payload + payloadlen;
+
+ assert((payloadlen % sizeof(uint32_t)) == 0);
+
+ for (; payload != end; payload += sizeof(uint32_t)) {
+ *dest++ = ngtcp2_get_uint32(payload);
+ }
+
+ return payloadlen / sizeof(uint32_t);
+}
+
+int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ const uint8_t *p = payload;
+
+ if (payloadlen <
+ NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ sr->rand = p;
+ sr->randlen = payloadlen - NGTCP2_STATELESS_RESET_TOKENLEN;
+ p += sr->randlen;
+ memcpy(sr->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN);
+
+ return 0;
+}
+
+int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = /* token */ 1 + NGTCP2_RETRY_TAGLEN;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dest->token.base = (uint8_t *)payload;
+ dest->token.len = (size_t)(payloadlen - NGTCP2_RETRY_TAGLEN);
+ ngtcp2_cpymem(dest->tag, payload + dest->token.len, NGTCP2_RETRY_TAGLEN);
+
+ return 0;
+}
+
+int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num,
+ size_t n) {
+ int64_t expected = max_pkt_num + 1;
+ int64_t win = (int64_t)1 << n;
+ int64_t hwin = win / 2;
+ int64_t mask = win - 1;
+ int64_t cand = (expected & ~mask) | pkt_num;
+
+ if (cand <= expected - hwin) {
+ assert(cand <= (int64_t)NGTCP2_MAX_VARINT - win);
+ return cand + win;
+ }
+ if (cand > expected + hwin && cand >= win) {
+ return cand - win;
+ }
+ return cand;
+}
+
+int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr) {
+ int64_t largest_ack = fr->largest_ack;
+ size_t i;
+
+ if (largest_ack < (int64_t)fr->first_ack_blklen) {
+ return NGTCP2_ERR_ACK_FRAME;
+ }
+
+ largest_ack -= (int64_t)fr->first_ack_blklen;
+
+ for (i = 0; i < fr->num_blks; ++i) {
+ if (largest_ack < (int64_t)fr->blks[i].gap + 2) {
+ return NGTCP2_ERR_ACK_FRAME;
+ }
+
+ largest_ack -= (int64_t)fr->blks[i].gap + 2;
+
+ if (largest_ack < (int64_t)fr->blks[i].blklen) {
+ return NGTCP2_ERR_ACK_FRAME;
+ }
+
+ largest_ack -= (int64_t)fr->blks[i].blklen;
+ }
+
+ return 0;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen,
+ const uint8_t *stateless_reset_token,
+ const uint8_t *rand, size_t randlen) {
+ uint8_t *p;
+
+ if (destlen <
+ NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ if (randlen < NGTCP2_MIN_STATELESS_RESET_RANDLEN) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = dest;
+
+ randlen = ngtcp2_min(destlen - NGTCP2_STATELESS_RESET_TOKENLEN, randlen);
+
+ p = ngtcp2_cpymem(p, rand, randlen);
+ p = ngtcp2_cpymem(p, stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ *dest = (uint8_t)((*dest & 0x7fu) | 0x40u);
+
+ return p - dest;
+}
+
+ngtcp2_ssize ngtcp2_pkt_write_retry(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token,
+ size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx) {
+ ngtcp2_pkt_hd hd;
+ uint8_t pseudo_retry[1500];
+ ngtcp2_ssize pseudo_retrylen;
+ uint8_t tag[NGTCP2_RETRY_TAGLEN];
+ int rv;
+ uint8_t *p;
+ size_t offset;
+ const uint8_t *nonce;
+ size_t noncelen;
+
+ assert(tokenlen > 0);
+ assert(!ngtcp2_cid_eq(scid, odcid));
+
+ /* 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) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_RETRY, dcid,
+ scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version,
+ /* len = */ 0);
+
+ pseudo_retrylen =
+ ngtcp2_pkt_encode_pseudo_retry(pseudo_retry, sizeof(pseudo_retry), &hd,
+ /* unused = */ 0, odcid, token, tokenlen);
+ if (pseudo_retrylen < 0) {
+ return pseudo_retrylen;
+ }
+
+ if (version == NGTCP2_PROTO_VER_V1) {
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ } else {
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ /* OpenSSL does not like NULL plaintext. */
+ rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen,
+ pseudo_retry, (size_t)pseudo_retrylen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ offset = 1 + odcid->datalen;
+ if (destlen < (size_t)pseudo_retrylen + sizeof(tag) - offset) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = ngtcp2_cpymem(dest, pseudo_retry + offset,
+ (size_t)pseudo_retrylen - offset);
+ p = ngtcp2_cpymem(p, tag, sizeof(tag));
+
+ return p - dest;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry(
+ uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused,
+ const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen) {
+ uint8_t *p = dest;
+ ngtcp2_ssize nwrite;
+
+ if (destlen < 1 + odcid->datalen) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *p++ = (uint8_t)odcid->datalen;
+ p = ngtcp2_cpymem(p, odcid->data, odcid->datalen);
+ destlen -= (size_t)(p - dest);
+
+ nwrite = ngtcp2_pkt_encode_hd_long(p, destlen, hd);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (destlen < (size_t)nwrite + tokenlen) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *p &= 0xf0;
+ *p |= unused;
+
+ p += nwrite;
+
+ p = ngtcp2_cpymem(p, token, tokenlen);
+
+ return p - dest;
+}
+
+int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_encrypt encrypt,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx) {
+ uint8_t pseudo_retry[1500];
+ size_t pseudo_retrylen;
+ uint8_t *p = pseudo_retry;
+ int rv;
+ uint8_t tag[NGTCP2_RETRY_TAGLEN];
+ const uint8_t *nonce;
+ size_t noncelen;
+
+ assert(pktlen >= sizeof(retry->tag));
+
+ if (sizeof(pseudo_retry) <
+ 1 + retry->odcid.datalen + pktlen - sizeof(retry->tag)) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ *p++ = (uint8_t)retry->odcid.datalen;
+ p = ngtcp2_cpymem(p, retry->odcid.data, retry->odcid.datalen);
+ p = ngtcp2_cpymem(p, pkt, pktlen - sizeof(retry->tag));
+
+ pseudo_retrylen = (size_t)(p - pseudo_retry);
+
+ if (version == NGTCP2_PROTO_VER_V1) {
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ } else {
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ /* OpenSSL does not like NULL plaintext. */
+ rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen,
+ pseudo_retry, pseudo_retrylen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (0 != memcmp(retry->tag, tag, sizeof(retry->tag))) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
+ size_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);
+
+ if (left <= n) {
+ return (size_t)-1;
+ }
+
+ left -= n;
+
+ if (left > 8 + 1073741823 && len > 1073741823) {
+#if SIZE_MAX > UINT32_MAX
+ len = ngtcp2_min(len, 4611686018427387903lu);
+#endif /* SIZE_MAX > UINT32_MAX */
+ return ngtcp2_min(len, left - 8);
+ }
+
+ if (left > 4 + 16383 && len > 16383) {
+ len = ngtcp2_min(len, 1073741823);
+ return ngtcp2_min(len, left - 4);
+ }
+
+ if (left > 2 + 63 && len > 63) {
+ len = ngtcp2_min(len, 16383);
+ return ngtcp2_min(len, left - 2);
+ }
+
+ len = ngtcp2_min(len, 63);
+ return ngtcp2_min(len, left - 1);
+}
+
+size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) {
+ size_t n = 1 /* type */ + ngtcp2_put_varint_len(offset);
+
+ /* CRYPTO frame must contain nonzero length data. Return -1 if
+ there is no space to write crypto data. */
+ if (left <= n + 1) {
+ return (size_t)-1;
+ }
+
+ left -= n;
+
+ if (left > 8 + 1073741823 && len > 1073741823) {
+#if SIZE_MAX > UINT32_MAX
+ len = ngtcp2_min(len, 4611686018427387903lu);
+#endif /* SIZE_MAX > UINT32_MAX */
+ return ngtcp2_min(len, left - 8);
+ }
+
+ if (left > 4 + 16383 && len > 16383) {
+ len = ngtcp2_min(len, 1073741823);
+ return ngtcp2_min(len, left - 4);
+ }
+
+ if (left > 2 + 63 && len > 63) {
+ len = ngtcp2_min(len, 16383);
+ return ngtcp2_min(len, left - 2);
+ }
+
+ len = ngtcp2_min(len, 63);
+ return ngtcp2_min(len, left - 1);
+}
+
+size_t ngtcp2_pkt_datagram_framelen(size_t len) {
+ return 1 /* type */ + ngtcp2_put_varint_len((uint64_t)len) + len;
+}
+
+uint8_t ngtcp2_pkt_get_type_long(uint8_t c) {
+ return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
+}
+
+int ngtcp2_pkt_verify_reserved_bits(uint8_t c) {
+ if (c & NGTCP2_HEADER_FORM_BIT) {
+ return (c & NGTCP2_LONG_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO;
+ }
+ return (c & NGTCP2_SHORT_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h
new file mode 100644
index 0000000000..708e0f6df3
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h
@@ -0,0 +1,1195 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PKT_H
+#define NGTCP2_PKT_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/* QUIC header macros */
+#define NGTCP2_HEADER_FORM_BIT 0x80
+#define NGTCP2_FIXED_BIT_MASK 0x40
+#define NGTCP2_PKT_NUMLEN_MASK 0x03
+
+/* Long header specific macros */
+#define NGTCP2_LONG_TYPE_MASK 0x30
+#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0c
+
+/* Short header specific macros */
+#define NGTCP2_SHORT_SPIN_BIT_MASK 0x20
+#define NGTCP2_SHORT_RESERVED_BIT_MASK 0x18
+#define NGTCP2_SHORT_KEY_PHASE_BIT 0x04
+
+/* NGTCP2_SR_TYPE is a Type field of Stateless Reset. */
+#define NGTCP2_SR_TYPE 0x1f
+
+/* NGTCP2_MIN_LONG_HEADERLEN is the minimum length of long header.
+ That is (1|1|TT|RR|PP)<1> + VERSION<4> + DCIL<1> + SCIL<1> +
+ LENGTH<1> + PKN<1> */
+#define NGTCP2_MIN_LONG_HEADERLEN (1 + 4 + 1 + 1 + 1 + 1)
+
+#define NGTCP2_STREAM_FIN_BIT 0x01
+#define NGTCP2_STREAM_LEN_BIT 0x02
+#define NGTCP2_STREAM_OFF_BIT 0x04
+
+/* NGTCP2_STREAM_OVERHEAD is the maximum number of bytes required
+ other than payload for STREAM frame. That is from type field to
+ the beginning of the payload. */
+#define NGTCP2_STREAM_OVERHEAD (1 + 8 + 8 + 8)
+
+/* NGTCP2_CRYPTO_OVERHEAD is the maximum number of bytes required
+ other than payload for CRYPTO frame. That is from type field to
+ the beginning of the payload. */
+#define NGTCP2_CRYPTO_OVERHEAD (1 + 8 + 8)
+
+/* NGTCP2_DATAGRAM_OVERHEAD is the maximum number of bytes required
+ other than payload for DATAGRAM frame. That is from type field to
+ the beginning of the payload. */
+#define NGTCP2_DATAGRAM_OVERHEAD (1 + 8)
+
+/* NGTCP2_MIN_FRAME_PAYLOADLEN is the minimum frame payload length. */
+#define NGTCP2_MIN_FRAME_PAYLOADLEN 16
+
+/* NGTCP2_MAX_SERVER_STREAM_ID_BIDI is the maximum bidirectional
+ server stream ID. */
+#define NGTCP2_MAX_SERVER_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffdll)
+/* NGTCP2_MAX_CLIENT_STREAM_ID_BIDI is the maximum bidirectional
+ client stream ID. */
+#define NGTCP2_MAX_CLIENT_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffcll)
+/* NGTCP2_MAX_SERVER_STREAM_ID_UNI is the maximum unidirectional
+ server stream ID. */
+#define NGTCP2_MAX_SERVER_STREAM_ID_UNI ((int64_t)0x3fffffffffffffffll)
+/* NGTCP2_MAX_CLIENT_STREAM_ID_UNI is the maximum unidirectional
+ client stream ID. */
+#define NGTCP2_MAX_CLIENT_STREAM_ID_UNI ((int64_t)0x3ffffffffffffffell)
+
+/* NGTCP2_MAX_NUM_ACK_BLK is the maximum number of Additional ACK
+ blocks which this library can create, or decode. */
+#define NGTCP2_MAX_ACK_BLKS 32
+
+/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */
+#define NGTCP2_MAX_PKT_NUM ((int64_t)((1ll << 62) - 1))
+
+/* NGTCP2_MIN_PKT_EXPANDLEN is the minimum packet size expansion in
+ addition to the minimum DCID length to hide/trigger Stateless
+ Reset. */
+#define NGTCP2_MIN_PKT_EXPANDLEN 22
+
+/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */
+#define NGTCP2_RETRY_TAGLEN 16
+
+typedef struct ngtcp2_pkt_retry {
+ ngtcp2_cid odcid;
+ ngtcp2_vec token;
+ uint8_t tag[NGTCP2_RETRY_TAGLEN];
+} ngtcp2_pkt_retry;
+
+typedef enum {
+ NGTCP2_FRAME_PADDING = 0x00,
+ NGTCP2_FRAME_PING = 0x01,
+ NGTCP2_FRAME_ACK = 0x02,
+ NGTCP2_FRAME_ACK_ECN = 0x03,
+ NGTCP2_FRAME_RESET_STREAM = 0x04,
+ NGTCP2_FRAME_STOP_SENDING = 0x05,
+ NGTCP2_FRAME_CRYPTO = 0x06,
+ NGTCP2_FRAME_NEW_TOKEN = 0x07,
+ NGTCP2_FRAME_STREAM = 0x08,
+ NGTCP2_FRAME_MAX_DATA = 0x10,
+ NGTCP2_FRAME_MAX_STREAM_DATA = 0x11,
+ NGTCP2_FRAME_MAX_STREAMS_BIDI = 0x12,
+ NGTCP2_FRAME_MAX_STREAMS_UNI = 0x13,
+ NGTCP2_FRAME_DATA_BLOCKED = 0x14,
+ NGTCP2_FRAME_STREAM_DATA_BLOCKED = 0x15,
+ NGTCP2_FRAME_STREAMS_BLOCKED_BIDI = 0x16,
+ NGTCP2_FRAME_STREAMS_BLOCKED_UNI = 0x17,
+ NGTCP2_FRAME_NEW_CONNECTION_ID = 0x18,
+ NGTCP2_FRAME_RETIRE_CONNECTION_ID = 0x19,
+ NGTCP2_FRAME_PATH_CHALLENGE = 0x1a,
+ NGTCP2_FRAME_PATH_RESPONSE = 0x1b,
+ NGTCP2_FRAME_CONNECTION_CLOSE = 0x1c,
+ NGTCP2_FRAME_CONNECTION_CLOSE_APP = 0x1d,
+ NGTCP2_FRAME_HANDSHAKE_DONE = 0x1e,
+ NGTCP2_FRAME_DATAGRAM = 0x30,
+ NGTCP2_FRAME_DATAGRAM_LEN = 0x31,
+} ngtcp2_frame_type;
+
+typedef struct ngtcp2_stream {
+ uint8_t type;
+ /**
+ * flags of decoded STREAM frame. This gets ignored when encoding
+ * STREAM frame.
+ */
+ uint8_t flags;
+ uint8_t fin;
+ int64_t stream_id;
+ uint64_t offset;
+ /* datacnt is the number of elements that data contains. Although
+ the length of data is 1 in this definition, the library may
+ allocate extra bytes to hold more elements. */
+ size_t datacnt;
+ /* data is the array of ngtcp2_vec which references data. */
+ ngtcp2_vec data[1];
+} ngtcp2_stream;
+
+typedef struct ngtcp2_ack_blk {
+ uint64_t gap;
+ uint64_t blklen;
+} ngtcp2_ack_blk;
+
+typedef struct ngtcp2_ack {
+ uint8_t type;
+ int64_t largest_ack;
+ uint64_t ack_delay;
+ /**
+ * ack_delay_unscaled is an ack_delay multiplied by
+ * 2**ack_delay_component * NGTCP2_MICROSECONDS.
+ */
+ ngtcp2_duration ack_delay_unscaled;
+ struct {
+ uint64_t ect0;
+ uint64_t ect1;
+ uint64_t ce;
+ } ecn;
+ uint64_t first_ack_blklen;
+ size_t num_blks;
+ ngtcp2_ack_blk blks[1];
+} ngtcp2_ack;
+
+typedef struct ngtcp2_padding {
+ uint8_t type;
+ /**
+ * The length of contiguous PADDING frames.
+ */
+ size_t len;
+} ngtcp2_padding;
+
+typedef struct ngtcp2_reset_stream {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t app_error_code;
+ uint64_t final_size;
+} ngtcp2_reset_stream;
+
+typedef struct ngtcp2_connection_close {
+ uint8_t type;
+ uint64_t error_code;
+ uint64_t frame_type;
+ size_t reasonlen;
+ uint8_t *reason;
+} ngtcp2_connection_close;
+
+typedef struct ngtcp2_max_data {
+ uint8_t type;
+ /**
+ * max_data is Maximum Data.
+ */
+ uint64_t max_data;
+} ngtcp2_max_data;
+
+typedef struct ngtcp2_max_stream_data {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t max_stream_data;
+} ngtcp2_max_stream_data;
+
+typedef struct ngtcp2_max_streams {
+ uint8_t type;
+ uint64_t max_streams;
+} ngtcp2_max_streams;
+
+typedef struct ngtcp2_ping {
+ uint8_t type;
+} ngtcp2_ping;
+
+typedef struct ngtcp2_data_blocked {
+ uint8_t type;
+ uint64_t offset;
+} ngtcp2_data_blocked;
+
+typedef struct ngtcp2_stream_data_blocked {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t offset;
+} ngtcp2_stream_data_blocked;
+
+typedef struct ngtcp2_streams_blocked {
+ uint8_t type;
+ uint64_t max_streams;
+} ngtcp2_streams_blocked;
+
+typedef struct ngtcp2_new_connection_id {
+ uint8_t type;
+ uint64_t seq;
+ uint64_t retire_prior_to;
+ ngtcp2_cid cid;
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+} ngtcp2_new_connection_id;
+
+typedef struct ngtcp2_stop_sending {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t app_error_code;
+} ngtcp2_stop_sending;
+
+typedef struct ngtcp2_path_challenge {
+ uint8_t type;
+ uint8_t data[8];
+} ngtcp2_path_challenge;
+
+typedef struct ngtcp2_path_response {
+ uint8_t type;
+ uint8_t data[8];
+} ngtcp2_path_response;
+
+typedef struct ngtcp2_crypto {
+ uint8_t type;
+ uint64_t offset;
+ /* datacnt is the number of elements that data contains. Although
+ the length of data is 1 in this definition, the library may
+ allocate extra bytes to hold more elements. */
+ size_t datacnt;
+ /* data is the array of ngtcp2_vec which references data. */
+ ngtcp2_vec data[1];
+} ngtcp2_crypto;
+
+typedef struct ngtcp2_new_token {
+ uint8_t type;
+ ngtcp2_vec token;
+} ngtcp2_new_token;
+
+typedef struct ngtcp2_retire_connection_id {
+ uint8_t type;
+ uint64_t seq;
+} ngtcp2_retire_connection_id;
+
+typedef struct ngtcp2_handshake_done {
+ uint8_t type;
+} ngtcp2_handshake_done;
+
+typedef struct ngtcp2_datagram {
+ uint8_t type;
+ /* datacnt is the number of elements that data contains. */
+ size_t datacnt;
+ /* data is a pointer to ngtcp2_vec array that stores data. */
+ ngtcp2_vec *data;
+ /* rdata is conveniently embedded to ngtcp2_datagram, so that data
+ field can just point to the address of this field to store a
+ single vector which is the case when DATAGRAM is received from a
+ remote endpoint. */
+ ngtcp2_vec rdata[1];
+} ngtcp2_datagram;
+
+typedef union ngtcp2_frame {
+ uint8_t type;
+ ngtcp2_stream stream;
+ ngtcp2_ack ack;
+ ngtcp2_padding padding;
+ ngtcp2_reset_stream reset_stream;
+ ngtcp2_connection_close connection_close;
+ ngtcp2_max_data max_data;
+ ngtcp2_max_stream_data max_stream_data;
+ ngtcp2_max_streams max_streams;
+ ngtcp2_ping ping;
+ ngtcp2_data_blocked data_blocked;
+ ngtcp2_stream_data_blocked stream_data_blocked;
+ ngtcp2_streams_blocked streams_blocked;
+ ngtcp2_new_connection_id new_connection_id;
+ ngtcp2_stop_sending stop_sending;
+ ngtcp2_path_challenge path_challenge;
+ ngtcp2_path_response path_response;
+ ngtcp2_crypto crypto;
+ ngtcp2_new_token new_token;
+ ngtcp2_retire_connection_id retire_connection_id;
+ ngtcp2_handshake_done handshake_done;
+ ngtcp2_datagram datagram;
+} ngtcp2_frame;
+
+typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain;
+
+/*
+ * ngtcp2_pkt_chain is the chain of incoming packets buffered.
+ */
+struct ngtcp2_pkt_chain {
+ ngtcp2_path_storage path;
+ ngtcp2_pkt_info pi;
+ ngtcp2_pkt_chain *next;
+ uint8_t *pkt;
+ /* pktlen is length of a QUIC packet. */
+ size_t pktlen;
+ /* dgramlen is length of UDP datagram that a QUIC packet is
+ included. */
+ size_t dgramlen;
+ ngtcp2_tstamp ts;
+};
+
+/*
+ * ngtcp2_pkt_chain_new allocates ngtcp2_pkt_chain objects, and
+ * assigns its pointer to |*ppc|. The content of buffer pointed by
+ * |pkt| of length |pktlen| is copied into |*ppc|. The packet is
+ * obtained via the network |path|. The values of path->local and
+ * path->remote are copied into |*ppc|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen, ngtcp2_tstamp ts,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pkt_chain_del deallocates |pc|. It also frees the memory
+ * pointed by |pc|.
+ */
+void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pkt_hd_init initializes |hd| with the given values. If
+ * |dcid| and/or |scid| is NULL, DCID and SCID of |hd| is empty
+ * respectively. |pkt_numlen| is the number of bytes used to encode
+ * |pkt_num| and either 1, 2, or 4. |version| is QUIC version for
+ * long header. |len| is the length field of Initial, 0RTT, and
+ * Handshake packets.
+ */
+void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type,
+ const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ int64_t pkt_num, size_t pkt_numlen, uint32_t version,
+ size_t len);
+
+/*
+ * ngtcp2_pkt_encode_hd_long encodes |hd| as QUIC long header into
+ * |out| which has length |outlen|. It returns the number of bytes
+ * written into |outlen| if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too short
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd);
+
+/*
+ * ngtcp2_pkt_encode_hd_short encodes |hd| as QUIC short header into
+ * |out| which has length |outlen|. It returns the number of bytes
+ * written into |outlen| if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too short
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_decode_frame` decodes a QUIC frame from the buffer
+ * pointed by |payload| whose length is |payloadlen|.
+ *
+ * This function returns the number of bytes read to decode a single
+ * frame if it succeeds, or one of the following negative error codes:
+ *
+ * :enum:`NGTCP2_ERR_FRAME_ENCODING`
+ * Frame is badly formatted; or frame type is unknown.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
+ size_t payloadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_encode_frame` encodes a frame |fm| into the buffer
+ * pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :enum:`NGTCP2_ERR_NOBUF`
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
+ ngtcp2_frame *fr);
+
+/*
+ * ngtcp2_pkt_decode_version_negotiation decodes Version Negotiation
+ * packet payload |payload| of length |payloadlen|, and stores the
+ * result in |dest|. |dest| must have enough capacity to store the
+ * result. |payloadlen| also must be a multiple of sizeof(uint32_t).
+ *
+ * This function returns the number of versions written in |dest|.
+ */
+size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stateless_reset decodes Stateless Reset payload
+ * |payload| of length |payloadlen|. The |payload| must start with
+ * Stateless Reset Token.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Payloadlen is too short.
+ */
+int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_retry decodes Retry packet payload |payload| of
+ * length |payloadlen|. The |payload| must start with Retry token
+ * field.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Payloadlen is too short.
+ */
+int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stream_frame decodes STREAM frame from |payload|
+ * of length |payloadlen|. The result is stored in the object pointed
+ * by |dest|. STREAM frame must start at payload[0]. This function
+ * finishes when it decodes one STREAM frame, and returns the exact
+ * number of bytes read to decode a frame if it succeeds, or one of
+ * the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STREAM frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_ack_frame decodes ACK frame from |payload| of
+ * length |payloadlen|. The result is stored in the object pointed by
+ * |dest|. ACK frame must start at payload[0]. This function
+ * finishes when it decodes one ACK frame, and returns the exact
+ * number of bytes read to decode a frame if it succeeds, or one of
+ * the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include ACK frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_padding_frame decodes contiguous PADDING frames
+ * from |payload| of length |payloadlen|. It continues to parse
+ * frames as long as the frame type is PADDING. This finishes when it
+ * encounters the frame type which is not PADDING, or all input data
+ * is read. The first byte (payload[0]) must be NGTCP2_FRAME_PADDING.
+ * This function returns the exact number of bytes read to decode
+ * PADDING frames.
+ */
+size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_reset_stream_frame decodes RESET_STREAM frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. RESET_STREAM frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * RESET_STREAM frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include RESET_STREAM frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_connection_close_frame decodes CONNECTION_CLOSE
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. CONNECTION_CLOSE frame must start
+ * at payload[0]. This function finishes it decodes one
+ * CONNECTION_CLOSE frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include CONNECTION_CLOSE frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame(
+ ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_max_data_frame decodes MAX_DATA frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. MAX_DATA frame must start at payload[0].
+ * This function finishes when it decodes one MAX_DATA frame, and
+ * returns the exact number of bytes read to decode a frame if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include MAX_DATA frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_max_stream_data_frame decodes MAX_STREAM_DATA
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. MAX_STREAM_DATA frame must start
+ * at payload[0]. This function finishes when it decodes one
+ * MAX_STREAM_DATA frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include MAX_STREAM_DATA frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame(
+ ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_max_streams_frame decodes MAX_STREAMS frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. MAX_STREAMS frame must start at
+ * payload[0]. This function finishes when it decodes one MAX_STREAMS
+ * frame, and returns the exact number of bytes read to decode a frame
+ * if it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include MAX_STREAMS frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_ping_frame decodes PING frame from |payload| of
+ * length |payloadlen|. The result is stored in the object pointed by
+ * |dest|. PING frame must start at payload[0]. This function
+ * finishes when it decodes one PING frame, and returns the exact
+ * number of bytes read to decode a frame if it succeeds, or one of
+ * the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include PING frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_data_blocked_frame decodes DATA_BLOCKED frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. DATA_BLOCKED frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * DATA_BLOCKED frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include DATA_BLOCKED frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stream_data_blocked_frame decodes
+ * STREAM_DATA_BLOCKED frame from |payload| of length |payloadlen|.
+ * The result is stored in the object pointed by |dest|.
+ * STREAM_DATA_BLOCKED frame must start at payload[0]. This function
+ * finishes when it decodes one STREAM_DATA_BLOCKED frame, and returns
+ * the exact number of bytes read to decode a frame if it succeeds, or
+ * one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STREAM_DATA_BLOCKED frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_streams_blocked_frame decodes STREAMS_BLOCKED
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. STREAMS_BLOCKED frame must start
+ * at payload[0]. This function finishes when it decodes one
+ * STREAMS_BLOCKED frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STREAMS_BLOCKED frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame(
+ ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_new_connection_id_frame decodes NEW_CONNECTION_ID
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. NEW_CONNECTION_ID frame must
+ * start at payload[0]. This function finishes when it decodes one
+ * NEW_CONNECTION_ID frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include NEW_CONNECTION_ID frame; or the
+ * length of CID is strictly less than NGTCP2_MIN_CIDLEN or
+ * greater than NGTCP2_MAX_CIDLEN.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame(
+ ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stop_sending_frame decodes STOP_SENDING frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. STOP_SENDING frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * STOP_SENDING frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STOP_SENDING frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_path_challenge_frame decodes PATH_CHALLENGE frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. PATH_CHALLENGE frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * PATH_CHALLENGE frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include PATH_CHALLENGE frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_path_response_frame decodes PATH_RESPONSE frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. PATH_RESPONSE frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * PATH_RESPONSE frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include PATH_RESPONSE frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_crypto_frame decodes CRYPTO frame from |payload|
+ * of length |payloadlen|. The result is stored in the object pointed
+ * by |dest|. CRYPTO frame must start at payload[0]. This function
+ * finishes when it decodes one CRYPTO frame, and returns the exact
+ * number of bytes read to decode a frame if it succeeds, or one of
+ * the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include CRYPTO frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_new_token_frame decodes NEW_TOKEN frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. NEW_TOKEN frame must start at
+ * payload[0]. This function finishes when it decodes one NEW_TOKEN
+ * frame, and returns the exact number of bytes read to decode a frame
+ * if it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include NEW_TOKEN frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_retire_connection_id_frame decodes RETIRE_CONNECTION_ID
+ * frame from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. RETIRE_CONNECTION_ID frame must start at
+ * payload[0]. This function finishes when it decodes one RETIRE_CONNECTION_ID
+ * frame, and returns the exact number of bytes read to decode a frame
+ * if it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include RETIRE_CONNECTION_ID frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_handshake_done_frame decodes HANDSHAKE_DONE frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. HANDSHAKE_DONE frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * HANDSHAKE_DONE frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include HANDSHAKE_DONE frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_datagram_frame decodes DATAGRAM frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. DATAGRAM frame must start at payload[0].
+ * This function finishes when it decodes one DATAGRAM frame, and
+ * returns the exact number of bytes read to decode a frame if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include DATAGRAM frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function assigns <the serialized frame type> &
+ * ~NGTCP2_FRAME_STREAM to fr->flags.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen,
+ ngtcp2_stream *fr);
+
+/*
+ * ngtcp2_pkt_encode_ack_frame encodes ACK frame |fr| into the buffer
+ * pointed by |out| of length |outlen|.
+ *
+ * This function assigns <the serialized frame type> &
+ * ~NGTCP2_FRAME_ACK to fr->flags.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen,
+ ngtcp2_ack *fr);
+
+/*
+ * ngtcp2_pkt_encode_padding_frame encodes PADDING frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function encodes consecutive fr->len PADDING frames.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write frame(s).
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_padding *fr);
+
+/*
+ * ngtcp2_pkt_encode_reset_stream_frame encodes RESET_STREAM frame
+ * |fr| into the buffer pointed by |out| of length |buflen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_reset_stream *fr);
+
+/*
+ * ngtcp2_pkt_encode_connection_close_frame encodes CONNECTION_CLOSE
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_connection_close *fr);
+
+/*
+ * ngtcp2_pkt_encode_max_data_frame encodes MAX_DATA frame |fr| into
+ * the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_data *fr);
+
+/*
+ * ngtcp2_pkt_encode_max_stream_data_frame encodes MAX_STREAM_DATA
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_stream_data *fr);
+
+/*
+ * ngtcp2_pkt_encode_max_streams_frame encodes MAX_STREAMS
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_streams *fr);
+
+/*
+ * ngtcp2_pkt_encode_ping_frame encodes PING frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_ping *fr);
+
+/*
+ * ngtcp2_pkt_encode_data_blocked_frame encodes DATA_BLOCKED frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_data_blocked *fr);
+
+/*
+ * ngtcp2_pkt_encode_stream_data_blocked_frame encodes
+ * STREAM_DATA_BLOCKED frame |fr| into the buffer pointed by |out| of
+ * length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr);
+
+/*
+ * ngtcp2_pkt_encode_streams_blocked_frame encodes STREAMS_BLOCKED
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_streams_blocked *fr);
+
+/*
+ * ngtcp2_pkt_encode_new_connection_id_frame encodes NEW_CONNECTION_ID
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_connection_id *fr);
+
+/*
+ * ngtcp2_pkt_encode_stop_sending_frame encodes STOP_SENDING frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_stop_sending *fr);
+
+/*
+ * ngtcp2_pkt_encode_path_challenge_frame encodes PATH_CHALLENGE frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_challenge *fr);
+
+/*
+ * ngtcp2_pkt_encode_path_response_frame encodes PATH_RESPONSE frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_response *fr);
+
+/*
+ * ngtcp2_pkt_encode_crypto_frame encodes CRYPTO frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_crypto *fr);
+
+/*
+ * ngtcp2_pkt_encode_new_token_frame encodes NEW_TOKEN frame |fr| into
+ * the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_token *fr);
+
+/*
+ * ngtcp2_pkt_encode_retire_connection_id_frame encodes RETIRE_CONNECTION_ID
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr);
+
+/*
+ * ngtcp2_pkt_encode_handshake_done_frame encodes HANDSHAKE_DONE frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_handshake_done *fr);
+
+/*
+ * ngtcp2_pkt_encode_datagram_frame encodes DATAGRAM frame |fr| into
+ * the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_datagram *fr);
+
+/*
+ * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for
+ * |pkt_num|, which is expected to be least significant |n| bits. The
+ * |max_pkt_num| is the highest successfully authenticated packet
+ * number.
+ */
+int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num,
+ size_t n);
+
+/*
+ * ngtcp2_pkt_validate_ack checks that ack is malformed or not.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed
+ */
+int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr);
+
+/*
+ * ngtcp2_pkt_stream_max_datalen returns the maximum number of bytes
+ * which can be sent for stream denoted by |stream_id|. |offset| is
+ * an offset of within the stream. |len| is the estimated number of
+ * bytes to be sent. |left| is the size of buffer. If |left| is too
+ * 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);
+
+/*
+ * ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes
+ * which can be sent for crypto stream. |offset| is an offset of
+ * within the crypto stream. |len| is the estimated number of bytes
+ * to be sent. |left| is the size of buffer. If |left| is too small
+ * to write CRYPTO frame, this function returns (size_t)-1.
+ */
+size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left);
+
+/*
+ * ngtcp2_pkt_datagram_framelen returns the length of DATAGRAM frame
+ * to encode |len| bytes of data.
+ */
+size_t ngtcp2_pkt_datagram_framelen(size_t len);
+
+/*
+ * ngtcp2_pkt_verify_reserved_bits verifies that the first byte |c| of
+ * the packet header has the correct reserved bits.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Reserved bits has wrong value.
+ */
+int ngtcp2_pkt_verify_reserved_bits(uint8_t c);
+
+/*
+ * ngtcp2_pkt_encode_pseudo_retry encodes Retry pseudo-packet in the
+ * buffer pointed by |dest| of length |destlen|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_BUF
+ * Buffer is too short.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry(
+ uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused,
+ const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen);
+
+/*
+ * ngtcp2_pkt_verify_retry_tag verifies Retry packet. The buffer
+ * pointed by |pkt| of length |pktlen| must contain Retry packet
+ * including packet header. The odcid and tag fields of |retry| must
+ * be specified. |aead| must be AEAD_AES_128_GCM.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Verification failed.
+ */
+int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_encrypt encrypt,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is
+ * the first byte of Long packet header.
+ */
+uint8_t ngtcp2_pkt_get_type_long(uint8_t c);
+
+#endif /* NGTCP2_PKT_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c
new file mode 100644
index 0000000000..47f4f10a29
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c
@@ -0,0 +1,230 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ppe.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_conv.h"
+
+void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen,
+ ngtcp2_crypto_cc *cc) {
+ ngtcp2_buf_init(&ppe->buf, out, outlen);
+
+ ppe->hdlen = 0;
+ ppe->len_offset = 0;
+ ppe->pkt_num_offset = 0;
+ ppe->pkt_numlen = 0;
+ ppe->pkt_num = 0;
+ ppe->sample_offset = 0;
+ ppe->cc = cc;
+}
+
+int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) {
+ ngtcp2_ssize rv;
+ ngtcp2_buf *buf = &ppe->buf;
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ ppe->len_offset = 1 + 4 + 1 + hd->dcid.datalen + 1 + hd->scid.datalen;
+ 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;
+ rv = ngtcp2_pkt_encode_hd_long(
+ buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
+ } else {
+ ppe->pkt_num_offset = 1 + hd->dcid.datalen;
+ rv = ngtcp2_pkt_encode_hd_short(
+ buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
+ }
+ if (rv < 0) {
+ return (int)rv;
+ }
+
+ ppe->sample_offset = ppe->pkt_num_offset + 4;
+
+ buf->last += rv;
+
+ ppe->pkt_numlen = hd->pkt_numlen;
+ ppe->hdlen = (size_t)rv;
+
+ ppe->pkt_num = hd->pkt_num;
+
+ return 0;
+}
+
+int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) {
+ ngtcp2_ssize rv;
+ ngtcp2_buf *buf = &ppe->buf;
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ rv = ngtcp2_pkt_encode_frame(
+ buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, fr);
+ if (rv < 0) {
+ return (int)rv;
+ }
+
+ buf->last += rv;
+
+ return 0;
+}
+
+ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) {
+ ngtcp2_buf *buf = &ppe->buf;
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ uint8_t *payload = buf->begin + ppe->hdlen;
+ size_t payloadlen = ngtcp2_buf_len(buf) - ppe->hdlen;
+ uint8_t mask[NGTCP2_HP_SAMPLELEN];
+ uint8_t *p;
+ size_t i;
+ int rv;
+
+ assert(cc->encrypt);
+ assert(cc->hp_mask);
+
+ if (ppe->len_offset) {
+ ngtcp2_put_varint14(
+ buf->begin + ppe->len_offset,
+ (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead));
+ }
+
+ ngtcp2_crypto_create_nonce(ppe->nonce, cc->ckm->iv.base, cc->ckm->iv.len,
+ ppe->pkt_num);
+
+ rv = cc->encrypt(payload, &cc->aead, &cc->ckm->aead_ctx, payload, payloadlen,
+ ppe->nonce, cc->ckm->iv.len, buf->begin, ppe->hdlen);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ buf->last = payload + payloadlen + cc->aead.max_overhead;
+
+ /* TODO Check that we have enough space to get sample */
+ assert(ppe->sample_offset + NGTCP2_HP_SAMPLELEN <= ngtcp2_buf_len(buf));
+
+ rv = cc->hp_mask(mask, &cc->hp, &cc->hp_ctx, buf->begin + ppe->sample_offset);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ p = buf->begin;
+ if (*p & NGTCP2_HEADER_FORM_BIT) {
+ *p = (uint8_t)(*p ^ (mask[0] & 0x0f));
+ } else {
+ *p = (uint8_t)(*p ^ (mask[0] & 0x1f));
+ }
+
+ p = buf->begin + ppe->pkt_num_offset;
+ for (i = 0; i < ppe->pkt_numlen; ++i) {
+ *(p + i) ^= mask[i + 1];
+ }
+
+ if (ppkt != NULL) {
+ *ppkt = buf->begin;
+ }
+
+ return (ngtcp2_ssize)ngtcp2_buf_len(buf);
+}
+
+size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ if (ngtcp2_buf_left(&ppe->buf) < cc->aead.max_overhead) {
+ return 0;
+ }
+
+ return ngtcp2_buf_left(&ppe->buf) - cc->aead.max_overhead;
+}
+
+size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ return ngtcp2_buf_len(&ppe->buf) + cc->aead.max_overhead;
+}
+
+size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ ngtcp2_buf *buf = &ppe->buf;
+ size_t len;
+
+ assert(ngtcp2_buf_left(buf) >= cc->aead.max_overhead);
+
+ len = ngtcp2_buf_left(buf) - cc->aead.max_overhead;
+ memset(buf->last, 0, len);
+ buf->last += len;
+
+ return len;
+}
+
+size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ ngtcp2_buf *buf = &ppe->buf;
+ size_t max_samplelen;
+ size_t len = 0;
+
+ assert(cc->aead.max_overhead);
+
+ max_samplelen =
+ ngtcp2_buf_len(buf) + cc->aead.max_overhead - ppe->sample_offset;
+ if (max_samplelen < NGTCP2_HP_SAMPLELEN) {
+ len = NGTCP2_HP_SAMPLELEN - max_samplelen;
+ assert(ngtcp2_ppe_left(ppe) >= len);
+ memset(buf->last, 0, len);
+ buf->last += len;
+ }
+
+ return len;
+}
+
+size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ ngtcp2_buf *buf = &ppe->buf;
+ size_t pktlen = ngtcp2_buf_len(buf) + cc->aead.max_overhead;
+ size_t len;
+
+ if (pktlen >= n) {
+ return 0;
+ }
+
+ len = n - pktlen;
+ buf->last = ngtcp2_setmem(buf->last, 0, len);
+
+ return len;
+}
+
+int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe) {
+ ngtcp2_buf *buf = &ppe->buf;
+ return ngtcp2_buf_left(buf) >= (4 - ppe->pkt_numlen) + NGTCP2_HP_SAMPLELEN;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h
new file mode 100644
index 0000000000..bf220df37c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h
@@ -0,0 +1,153 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PPE_H
+#define NGTCP2_PPE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_buf.h"
+#include "ngtcp2_crypto.h"
+
+/*
+ * ngtcp2_ppe is the Protected Packet Encoder.
+ */
+typedef struct ngtcp2_ppe {
+ ngtcp2_buf buf;
+ ngtcp2_crypto_cc *cc;
+ /* hdlen is the number of bytes for packet header written in buf. */
+ size_t hdlen;
+ /* len_offset is the offset to Length field. */
+ size_t len_offset;
+ /* pkt_num_offset is the offset to packet number field. */
+ size_t pkt_num_offset;
+ /* pkt_numlen is the number of bytes used to encode a packet
+ number */
+ size_t pkt_numlen;
+ /* sample_offset is the offset to sample for packet number
+ encryption. */
+ size_t sample_offset;
+ /* pkt_num is the packet number written in buf. */
+ int64_t pkt_num;
+ /* nonce is the buffer to store nonce. It should be equal or longer
+ than then length of IV. */
+ uint8_t nonce[32];
+} ngtcp2_ppe;
+
+/*
+ * ngtcp2_ppe_init initializes |ppe| with the given buffer.
+ */
+void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen,
+ ngtcp2_crypto_cc *cc);
+
+/*
+ * ngtcp2_ppe_encode_hd encodes |hd|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * The buffer is too small.
+ */
+int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd);
+
+/*
+ * ngtcp2_ppe_encode_frame encodes |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * The buffer is too small.
+ */
+int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr);
+
+/*
+ * ngtcp2_ppe_final encrypts QUIC packet payload. If |**ppkt| is not
+ * NULL, the pointer to the packet is assigned to it.
+ *
+ * This function returns the length of QUIC packet, including header,
+ * and payload if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt);
+
+/*
+ * ngtcp2_ppe_left returns the number of bytes left to write
+ * additional frames. This does not count AEAD overhead.
+ */
+size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe);
+
+/*
+ * ngtcp2_ppe_pktlen returns the provisional packet length. It
+ * includes AEAD overhead.
+ */
+size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe);
+
+/**
+ * @function
+ *
+ * `ngtcp2_ppe_padding` encodes PADDING frames to the end of the
+ * buffer. This function returns the number of bytes padded.
+ */
+size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe);
+
+/*
+ * ngtcp2_ppe_padding_hp_sample adds PADDING frame if the current
+ * payload does not have enough space for header protection sample.
+ * This function should be called just before calling
+ * ngtcp2_ppe_final().
+ *
+ * This function returns the number of bytes added as padding.
+ */
+size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe);
+
+/*
+ * ngtcp2_ppe_padding_size adds PADDING frame so that the size of QUIC
+ * packet is at least |n| bytes long. If it is unable to add PADDING
+ * in that way, this function still adds PADDING frame as much as
+ * possible. This function should be called just before calling
+ * ngtcp2_ppe_final(). For Short packet, this function should be
+ * called instead of ngtcp2_ppe_padding_hp_sample.
+ *
+ * This function returns the number of bytes added as padding.
+ */
+size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n);
+
+/*
+ * ngtcp2_ppe_ensure_hp_sample returns nonzero if the buffer has
+ * enough space for header protection sample. This should be called
+ * right after packet header is written.
+ */
+int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe);
+
+#endif /* NGTCP2_PPE_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c
new file mode 100644
index 0000000000..5e1003d794
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c
@@ -0,0 +1,164 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pq.h"
+
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+
+void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem) {
+ pq->mem = mem;
+ pq->capacity = 0;
+ pq->q = NULL;
+ pq->length = 0;
+ pq->less = less;
+}
+
+void ngtcp2_pq_free(ngtcp2_pq *pq) {
+ ngtcp2_mem_free(pq->mem, pq->q);
+ pq->q = NULL;
+}
+
+static void swap(ngtcp2_pq *pq, size_t i, size_t j) {
+ ngtcp2_pq_entry *a = pq->q[i];
+ ngtcp2_pq_entry *b = pq->q[j];
+
+ pq->q[i] = b;
+ b->index = i;
+ pq->q[j] = a;
+ a->index = j;
+}
+
+static void bubble_up(ngtcp2_pq *pq, size_t index) {
+ size_t parent;
+ while (index != 0) {
+ parent = (index - 1) / 2;
+ if (!pq->less(pq->q[index], pq->q[parent])) {
+ return;
+ }
+ swap(pq, parent, index);
+ index = parent;
+ }
+}
+
+int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item) {
+ if (pq->capacity <= pq->length) {
+ void *nq;
+ size_t ncapacity;
+
+ ncapacity = ngtcp2_max(4, (pq->capacity * 2));
+
+ nq = ngtcp2_mem_realloc(pq->mem, pq->q,
+ ncapacity * sizeof(ngtcp2_pq_entry *));
+ if (nq == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ pq->capacity = ncapacity;
+ pq->q = nq;
+ }
+ pq->q[pq->length] = item;
+ item->index = pq->length;
+ ++pq->length;
+ bubble_up(pq, pq->length - 1);
+ return 0;
+}
+
+ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq) {
+ assert(pq->length);
+ return pq->q[0];
+}
+
+static void bubble_down(ngtcp2_pq *pq, size_t index) {
+ size_t i, j, minindex;
+ for (;;) {
+ j = index * 2 + 1;
+ minindex = index;
+ for (i = 0; i < 2; ++i, ++j) {
+ if (j >= pq->length) {
+ break;
+ }
+ if (pq->less(pq->q[j], pq->q[minindex])) {
+ minindex = j;
+ }
+ }
+ if (minindex == index) {
+ return;
+ }
+ swap(pq, index, minindex);
+ index = minindex;
+ }
+}
+
+void ngtcp2_pq_pop(ngtcp2_pq *pq) {
+ if (pq->length > 0) {
+ pq->q[0] = pq->q[pq->length - 1];
+ pq->q[0]->index = 0;
+ --pq->length;
+ bubble_down(pq, 0);
+ }
+}
+
+void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item) {
+ assert(pq->q[item->index] == item);
+
+ if (item->index == 0) {
+ ngtcp2_pq_pop(pq);
+ return;
+ }
+
+ if (item->index == pq->length - 1) {
+ --pq->length;
+ return;
+ }
+
+ pq->q[item->index] = pq->q[pq->length - 1];
+ pq->q[item->index]->index = item->index;
+ --pq->length;
+
+ if (pq->less(item, pq->q[item->index])) {
+ bubble_down(pq, item->index);
+ } else {
+ bubble_up(pq, item->index);
+ }
+}
+
+int ngtcp2_pq_empty(ngtcp2_pq *pq) { return pq->length == 0; }
+
+size_t ngtcp2_pq_size(ngtcp2_pq *pq) { return pq->length; }
+
+int ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg) {
+ size_t i;
+
+ if (pq->length == 0) {
+ return 0;
+ }
+ for (i = 0; i < pq->length; ++i) {
+ if ((*fun)(pq->q[i], arg)) {
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h
new file mode 100644
index 0000000000..720c309f5a
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h
@@ -0,0 +1,126 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PQ_H
+#define NGTCP2_PQ_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+/* Implementation of priority queue */
+
+/* NGTCP2_PQ_BAD_INDEX is the priority queue index which indicates
+ that an entry is not queued. Assigning this value to
+ ngtcp2_pq_entry.index can check that the entry is queued or not. */
+#define NGTCP2_PQ_BAD_INDEX SIZE_MAX
+
+typedef struct ngtcp2_pq_entry {
+ size_t index;
+} ngtcp2_pq_entry;
+
+/* "less" function, return nonzero if |lhs| is less than |rhs|. */
+typedef int (*ngtcp2_less)(const ngtcp2_pq_entry *lhs,
+ const ngtcp2_pq_entry *rhs);
+
+typedef struct ngtcp2_pq {
+ /* The pointer to the pointer to the item stored */
+ ngtcp2_pq_entry **q;
+ /* Memory allocator */
+ const ngtcp2_mem *mem;
+ /* The number of items stored */
+ size_t length;
+ /* The maximum number of items this pq can store. This is
+ automatically extended when length is reached to this value. */
+ size_t capacity;
+ /* The less function between items */
+ ngtcp2_less less;
+} ngtcp2_pq;
+
+/*
+ * Initializes priority queue |pq| with compare function |cmp|.
+ */
+void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |pq|. The stored items are
+ * not freed by this function.
+ */
+void ngtcp2_pq_free(ngtcp2_pq *pq);
+
+/*
+ * Adds |item| to the priority queue |pq|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item);
+
+/*
+ * Returns item at the top of the queue |pq|. It is undefined if the
+ * queue is empty.
+ */
+ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq);
+
+/*
+ * Pops item at the top of the queue |pq|. The popped item is not
+ * freed by this function.
+ */
+void ngtcp2_pq_pop(ngtcp2_pq *pq);
+
+/*
+ * Returns nonzero if the queue |pq| is empty.
+ */
+int ngtcp2_pq_empty(ngtcp2_pq *pq);
+
+/*
+ * Returns the number of items in the queue |pq|.
+ */
+size_t ngtcp2_pq_size(ngtcp2_pq *pq);
+
+typedef int (*ngtcp2_pq_item_cb)(ngtcp2_pq_entry *item, void *arg);
+
+/*
+ * Applys |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.
+ * Otherwise this function returns 0.
+ */
+int ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg);
+
+/*
+ * Removes |item| from priority queue.
+ */
+void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item);
+
+#endif /* NGTCP2_PQ_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c
new file mode 100644
index 0000000000..d5f7759d6c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c
@@ -0,0 +1,180 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pv.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_addr.h"
+
+void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
+ ngtcp2_tstamp expiry, uint8_t flags) {
+ memcpy(pvent->data, data, sizeof(pvent->data));
+ pvent->expiry = expiry;
+ pvent->flags = flags;
+}
+
+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_dcid_copy(&(*ppv)->dcid, dcid);
+
+ (*ppv)->mem = mem;
+ (*ppv)->log = log;
+ (*ppv)->timeout = timeout;
+ (*ppv)->fallback_pto = 0;
+ (*ppv)->started_ts = UINT64_MAX;
+ (*ppv)->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT;
+ (*ppv)->round = 0;
+ (*ppv)->flags = flags;
+
+ return 0;
+}
+
+void ngtcp2_pv_del(ngtcp2_pv *pv) {
+ if (pv == NULL) {
+ return;
+ }
+ ngtcp2_ringbuf_free(&pv->ents);
+ ngtcp2_mem_free(pv->mem, pv);
+}
+
+void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
+ ngtcp2_tstamp expiry, uint8_t flags,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pv_entry *ent;
+
+ assert(pv->probe_pkt_left);
+
+ if (ngtcp2_ringbuf_len(&pv->ents) == 0) {
+ pv->started_ts = ts;
+ }
+
+ ent = ngtcp2_ringbuf_push_back(&pv->ents);
+ ngtcp2_pv_entry_init(ent, data, expiry, flags);
+
+ pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER;
+ --pv->probe_pkt_left;
+}
+
+int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
+ size_t len = ngtcp2_ringbuf_len(&pv->ents);
+ size_t i;
+ ngtcp2_pv_entry *ent;
+
+ if (len == 0) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ for (i = 0; i < len; ++i) {
+ ent = ngtcp2_ringbuf_get(&pv->ents, 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");
+ return 0;
+ }
+ }
+
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+}
+
+void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
+ ngtcp2_pv_entry *ent;
+
+ if (ngtcp2_ringbuf_len(&pv->ents) == 0) {
+ return;
+ }
+
+ ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+
+ if (ent->expiry > ts) {
+ return;
+ }
+
+ ++pv->round;
+ pv->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT;
+}
+
+int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv) {
+ return pv->probe_pkt_left > 0;
+}
+
+int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
+ ngtcp2_tstamp t;
+ ngtcp2_pv_entry *ent;
+
+ if (pv->started_ts == UINT64_MAX) {
+ return 0;
+ }
+
+ assert(ngtcp2_ringbuf_len(&pv->ents));
+
+ ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+
+ t = pv->started_ts + pv->timeout;
+ t = ngtcp2_max(t, ent->expiry);
+
+ return t <= ts;
+}
+
+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) {
+ return UINT64_MAX;
+ }
+
+ ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+
+ return ent->expiry;
+}
+
+void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
+ ngtcp2_tstamp expiry = ngtcp2_pv_next_expiry(pv);
+
+ if (expiry > ts) {
+ return;
+ }
+
+ pv->flags |= NGTCP2_PV_FLAG_CANCEL_TIMER;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h
new file mode 100644
index 0000000000..b532dbca98
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h
@@ -0,0 +1,193 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PV_H
+#define NGTCP2_PV_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_cid.h"
+#include "ngtcp2_ringbuf.h"
+
+/* NGTCP2_PV_MAX_ENTRIES is the maximum number of entries that
+ ngtcp2_pv can contain. It must be power of 2. */
+#define NGTCP2_PV_MAX_ENTRIES 8
+/* NGTCP2_PV_NUM_PROBE_PKT is the number of probe packets containing
+ PATH_CHALLENGE sent at a time. */
+#define NGTCP2_PV_NUM_PROBE_PKT 2
+
+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
+/* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which
+ contains PATH_CHALLENGE is undersized (< 1200 bytes) */
+#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01
+
+typedef struct ngtcp2_pv_entry {
+ /* expiry is the timestamp when this PATH_CHALLENGE expires. */
+ ngtcp2_tstamp expiry;
+ /* flags is zero or more of NGTCP2_PV_ENTRY_FLAG_*. */
+ uint8_t flags;
+ /* data is a byte string included in PATH_CHALLENGE. */
+ uint8_t data[8];
+} ngtcp2_pv_entry;
+
+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
+/* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path
+ validation should be ignored entirely. */
+#define NGTCP2_PV_FLAG_DONT_CARE 0x01
+/* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is
+ cancelled. */
+#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02
+/* 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
+/* 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
+
+typedef struct ngtcp2_pv ngtcp2_pv;
+
+/*
+ * ngtcp2_pv is the context of a single path validation.
+ */
+struct ngtcp2_pv {
+ const ngtcp2_mem *mem;
+ ngtcp2_log *log;
+ /* dcid is DCID and path this path validation uses. */
+ ngtcp2_dcid dcid;
+ /* fallback_dcid is the usually validated DCID and used as a
+ fallback if this path validation fails. */
+ ngtcp2_dcid fallback_dcid;
+ /* ents is the ring buffer of ngtcp2_pv_entry */
+ ngtcp2_ringbuf ents;
+ /* timeout is the duration within which this path validation should
+ succeed. */
+ ngtcp2_duration timeout;
+ /* fallback_pto is PTO of fallback connection. */
+ ngtcp2_duration fallback_pto;
+ /* started_ts is the timestamp this path validation starts. */
+ ngtcp2_tstamp started_ts;
+ /* round is the number of times that probe_pkt_left is reset. */
+ size_t round;
+ /* probe_pkt_left is the number of probe packets containing
+ PATH_CHALLENGE which can be send without waiting for an
+ expiration of a previous flight. */
+ size_t probe_pkt_left;
+ /* flags is bitwise-OR of zero or more of NGTCP2_PV_FLAG_*. */
+ uint8_t flags;
+};
+
+/*
+ * ngtcp2_pv_new creates new ngtcp2_pv object and assigns its pointer
+ * to |*ppv|. This function makes a copy of |dcid|. |timeout| is a
+ * duration within which this path validation must succeed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid,
+ ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pv_del deallocates |pv|. This function frees memory |pv|
+ * points too.
+ */
+void ngtcp2_pv_del(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_add_entry adds new entry with |data|. |expiry| is the
+ * expiry time of the entry.
+ */
+void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
+ ngtcp2_tstamp expiry, uint8_t flags, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pv_full returns nonzero if |pv| is full of ngtcp2_pv_entry.
+ */
+int ngtcp2_pv_full(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_validate validates that the received |data| matches the
+ * one of the existing entry. The flag of ngtcp2_pv_entry that
+ * matches |data| is assigned to |*pflags| if this function succeeds.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PATH_VALIDATION_FAILED
+ * path validation has failed and must be abandoned
+ * NGTCP2_ERR_INVALID_STATE
+ * |pv| includes no entry
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |pv| does not have an entry which has |data| and |path|
+ */
+int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data);
+
+/*
+ * ngtcp2_pv_handle_entry_expiry checks expiry of existing entries.
+ */
+void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pv_should_send_probe returns nonzero if new entry can be
+ * added by ngtcp2_pv_add_entry.
+ */
+int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_validation_timed_out returns nonzero if the path
+ * validation fails because of timeout.
+ */
+int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pv_next_expiry returns the earliest expiry.
+ */
+ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_cancel_expired_timer cancels the expired timer.
+ */
+void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts);
+
+#endif /* NGTCP2_PV_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c
new file mode 100644
index 0000000000..7c31ab64af
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c
@@ -0,0 +1,1108 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_qlog.h"
+
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_vec.h"
+
+void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write,
+ ngtcp2_tstamp ts, void *user_data) {
+ qlog->write = write;
+ qlog->ts = qlog->last_ts = ts;
+ qlog->user_data = user_data;
+}
+
+#define write_verbatim(DEST, S) ngtcp2_cpymem((DEST), (S), sizeof(S) - 1)
+
+static uint8_t *write_string_impl(uint8_t *p, const uint8_t *data,
+ size_t datalen) {
+ *p++ = '"';
+ if (datalen) {
+ p = ngtcp2_cpymem(p, data, datalen);
+ }
+ *p++ = '"';
+ return p;
+}
+
+#define write_string(DEST, S) \
+ write_string_impl((DEST), (const uint8_t *)(S), sizeof(S) - 1)
+
+#define NGTCP2_LOWER_XDIGITS "0123456789abcdef"
+
+static uint8_t *write_hex(uint8_t *p, const uint8_t *data, size_t datalen) {
+ const uint8_t *b = data, *end = data + datalen;
+ *p++ = '"';
+ for (; b != end; ++b) {
+ *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b >> 4];
+ *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b & 0xf];
+ }
+ *p++ = '"';
+ return p;
+}
+
+static uint8_t *write_cid(uint8_t *p, const ngtcp2_cid *cid) {
+ return write_hex(p, cid->data, cid->datalen);
+}
+
+static uint8_t *write_number(uint8_t *p, uint64_t n) {
+ size_t nlen = 0;
+ uint64_t t;
+ uint8_t *res;
+
+ if (n == 0) {
+ *p++ = '0';
+ return p;
+ }
+ for (t = n; t; t /= 10, ++nlen)
+ ;
+ p += nlen;
+ res = p;
+ for (; n; n /= 10) {
+ *--p = (uint8_t)((n % 10) + '0');
+ }
+ return res;
+}
+
+static uint8_t *write_tstamp(uint8_t *p, ngtcp2_tstamp ts) {
+ return write_number(p, ts / NGTCP2_MILLISECONDS);
+}
+
+static uint8_t *write_duration(uint8_t *p, ngtcp2_duration duration) {
+ return write_number(p, duration / NGTCP2_MILLISECONDS);
+}
+
+static uint8_t *write_bool(uint8_t *p, int b) {
+ if (b) {
+ return ngtcp2_cpymem(p, "true", sizeof("true") - 1);
+ }
+ return ngtcp2_cpymem(p, "false", sizeof("false") - 1);
+}
+
+static uint8_t *write_pair_impl(uint8_t *p, const uint8_t *name, size_t namelen,
+ const ngtcp2_vec *value) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_string_impl(p, value->base, value->len);
+}
+
+#define write_pair(DEST, NAME, VALUE) \
+ write_pair_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, (VALUE))
+
+static uint8_t *write_pair_hex_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, const uint8_t *value,
+ size_t valuelen) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_hex(p, value, valuelen);
+}
+
+#define write_pair_hex(DEST, NAME, VALUE, VALUELEN) \
+ write_pair_hex_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE), (VALUELEN))
+
+static uint8_t *write_pair_number_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, uint64_t value) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_number(p, value);
+}
+
+#define write_pair_number(DEST, NAME, VALUE) \
+ write_pair_number_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_duration_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen,
+ ngtcp2_duration duration) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_duration(p, duration);
+}
+
+#define write_pair_duration(DEST, NAME, VALUE) \
+ write_pair_duration_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_tstamp_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, ngtcp2_tstamp ts) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_tstamp(p, ts);
+}
+
+#define write_pair_tstamp(DEST, NAME, VALUE) \
+ write_pair_tstamp_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_bool_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, int b) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_bool(p, b);
+}
+
+#define write_pair_bool(DEST, NAME, VALUE) \
+ write_pair_bool_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, const ngtcp2_cid *cid) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_cid(p, cid);
+}
+
+#define write_pair_cid(DEST, NAME, VALUE) \
+ write_pair_cid_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+#define ngtcp2_make_vec_lit(S) \
+ { (uint8_t *)(S), sizeof((S)) - 1 }
+
+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 = write_cid(p, odcid);
+ *p++ = '}';
+ return p;
+}
+
+static uint8_t *write_trace(uint8_t *p, int server, const ngtcp2_cid *odcid) {
+ p = write_verbatim(
+ p, "\"trace\":{\"vantage_point\":{\"name\":\"ngtcp2\",\"type\":");
+ if (server) {
+ p = write_string(p, "server");
+ } else {
+ p = write_string(p, "client");
+ }
+ p = write_verbatim(p, "},");
+ p = write_common_fields(p, odcid);
+ *p++ = '}';
+ return p;
+}
+
+void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) {
+ uint8_t buf[1024];
+ uint8_t *p = buf;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ p = write_verbatim(
+ p, "{\"qlog_format\":\"NDJSON\",\"qlog_version\":\"draft-02\",");
+ p = write_trace(p, server, odcid);
+ p = write_verbatim(p, "}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_end(ngtcp2_qlog *qlog) {
+ uint8_t buf[1] = {0};
+
+ if (!qlog->write) {
+ return;
+ }
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_FIN, &buf, 0);
+}
+
+static ngtcp2_vec vec_pkt_type_initial = ngtcp2_make_vec_lit("initial");
+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_unknown = ngtcp2_make_vec_lit("unknown");
+
+static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) {
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ switch (hd->type) {
+ case NGTCP2_PKT_INITIAL:
+ return &vec_pkt_type_initial;
+ case NGTCP2_PKT_HANDSHAKE:
+ return &vec_pkt_type_handshake;
+ case NGTCP2_PKT_0RTT:
+ return &vec_pkt_type_0rtt;
+ case NGTCP2_PKT_RETRY:
+ return &vec_pkt_type_retry;
+ default:
+ return &vec_pkt_type_unknown;
+ }
+ }
+
+ return &vec_pkt_type_1rtt;
+}
+
+static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) {
+ /*
+ * {"packet_type":"version_negotiation","packet_number":"0000000000000000000"}
+ */
+#define NGTCP2_QLOG_PKT_HD_OVERHEAD 75
+
+ *p++ = '{';
+ p = write_pair(p, "packet_type", qlog_pkt_type(hd));
+ *p++ = ',';
+ p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num);
+ /* TODO Write DCIL and DCID */
+ /* TODO Write SCIL and SCID */
+ *p++ = '}';
+ return p;
+}
+
+static uint8_t *write_padding_frame(uint8_t *p, const ngtcp2_padding *fr) {
+ (void)fr;
+
+ /* {"frame_type":"padding"} */
+#define NGTCP2_QLOG_PADDING_FRAME_OVERHEAD 24
+
+ return write_verbatim(p, "{\"frame_type\":\"padding\"}");
+}
+
+static uint8_t *write_ping_frame(uint8_t *p, const ngtcp2_ping *fr) {
+ (void)fr;
+
+ /* {"frame_type":"ping"} */
+#define NGTCP2_QLOG_PING_FRAME_OVERHEAD 21
+
+ return write_verbatim(p, "{\"frame_type\":\"ping\"}");
+}
+
+static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
+ int64_t largest_ack, min_ack;
+ size_t i;
+ const ngtcp2_ack_blk *blk;
+
+ /*
+ * {"frame_type":"ack","ack_delay":0000000000000000000,"acked_ranges":[]}
+ *
+ * each range:
+ * [0000000000000000000,0000000000000000000],
+ *
+ * ecn:
+ * ,"ect1":0000000000000000000,"ect0":0000000000000000000,"ce":0000000000000000000
+ */
+#define NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD 70
+#define NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD 42
+#define NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD 79
+
+ 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++ = '[';
+
+ largest_ack = fr->largest_ack;
+ min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen;
+
+ *p++ = '[';
+ p = write_number(p, (uint64_t)min_ack);
+ if (largest_ack != min_ack) {
+ *p++ = ',';
+ p = write_number(p, (uint64_t)largest_ack);
+ }
+ *p++ = ']';
+
+ for (i = 0; i < fr->num_blks; ++i) {
+ blk = &fr->blks[i];
+ largest_ack = min_ack - (int64_t)blk->gap - 2;
+ min_ack = largest_ack - (int64_t)blk->blklen;
+ *p++ = ',';
+ *p++ = '[';
+ p = write_number(p, (uint64_t)min_ack);
+ if (largest_ack != min_ack) {
+ *p++ = ',';
+ p = write_number(p, (uint64_t)largest_ack);
+ }
+ *p++ = ']';
+ }
+
+ *p++ = ']';
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ *p++ = ',';
+ p = write_pair_number(p, "ect1", fr->ecn.ect1);
+ *p++ = ',';
+ p = write_pair_number(p, "ect0", fr->ecn.ect0);
+ *p++ = ',';
+ p = write_pair_number(p, "ce", fr->ecn.ce);
+ }
+
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_reset_stream_frame(uint8_t *p,
+ const ngtcp2_reset_stream *fr) {
+ /*
+ * {"frame_type":"reset_stream","stream_id":0000000000000000000,"error_code":0000000000000000000,"final_size":0000000000000000000}
+ */
+#define NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD 127
+
+ p = write_verbatim(p, "{\"frame_type\":\"reset_stream\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "error_code", fr->app_error_code);
+ *p++ = ',';
+ p = write_pair_number(p, "final_size", fr->final_size);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_stop_sending_frame(uint8_t *p,
+ const ngtcp2_stop_sending *fr) {
+ /*
+ * {"frame_type":"stop_sending","stream_id":0000000000000000000,"error_code":0000000000000000000}
+ */
+#define NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD 94
+
+ p = write_verbatim(p, "{\"frame_type\":\"stop_sending\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "error_code", fr->app_error_code);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) {
+ /*
+ * {"frame_type":"crypto","offset":0000000000000000000,"length":0000000000000000000}
+ */
+#define NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD 81
+
+ p = write_verbatim(p, "{\"frame_type\":\"crypto\",");
+ p = write_pair_number(p, "offset", fr->offset);
+ *p++ = ',';
+ p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) {
+ /*
+ * {"frame_type":"new_token","length":0000000000000000000,"token":""}
+ */
+#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66
+
+ 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++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_stream_frame(uint8_t *p, const ngtcp2_stream *fr) {
+ /*
+ * {"frame_type":"stream","stream_id":0000000000000000000,"offset":0000000000000000000,"length":0000000000000000000,"fin":true}
+ */
+#define NGTCP2_QLOG_STREAM_FRAME_OVERHEAD 124
+
+ p = write_verbatim(p, "{\"frame_type\":\"stream\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "offset", fr->offset);
+ *p++ = ',';
+ p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt));
+ if (fr->fin) {
+ *p++ = ',';
+ p = write_pair_bool(p, "fin", 1);
+ }
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_max_data_frame(uint8_t *p, const ngtcp2_max_data *fr) {
+ /*
+ * {"frame_type":"max_data","maximum":0000000000000000000}
+ */
+#define NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD 55
+
+ p = write_verbatim(p, "{\"frame_type\":\"max_data\",");
+ p = write_pair_number(p, "maximum", fr->max_data);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_max_stream_data_frame(uint8_t *p,
+ const ngtcp2_max_stream_data *fr) {
+ /*
+ * {"frame_type":"max_stream_data","stream_id":0000000000000000000,"maximum":0000000000000000000}
+ */
+#define NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD 94
+
+ p = write_verbatim(p, "{\"frame_type\":\"max_stream_data\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "maximum", fr->max_stream_data);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_max_streams_frame(uint8_t *p,
+ const ngtcp2_max_streams *fr) {
+ /*
+ * {"frame_type":"max_streams","stream_type":"unidirectional","maximum":0000000000000000000}
+ */
+#define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89
+
+ p = write_verbatim(p, "{\"frame_type\":\"max_streams\",");
+ p = write_string(p, "stream_type");
+ *p++ = ':';
+ if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) {
+ p = write_string(p, "bidirectional");
+ } else {
+ p = write_string(p, "unidirectional");
+ }
+ *p++ = ',';
+ p = write_pair_number(p, "maximum", fr->max_streams);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_data_blocked_frame(uint8_t *p,
+ const ngtcp2_data_blocked *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"data_blocked"}
+ */
+#define NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD 29
+
+ /* TODO log limit */
+
+ return write_verbatim(p, "{\"frame_type\":\"data_blocked\"}");
+}
+
+static uint8_t *
+write_stream_data_blocked_frame(uint8_t *p,
+ const ngtcp2_stream_data_blocked *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"stream_data_blocked"}
+ */
+#define NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD 36
+
+ /* TODO log limit */
+
+ return write_verbatim(p, "{\"frame_type\":\"stream_data_blocked\"}");
+}
+
+static uint8_t *write_streams_blocked_frame(uint8_t *p,
+ const ngtcp2_streams_blocked *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"streams_blocked"}
+ */
+#define NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD 32
+
+ /* TODO Log stream_type and limit */
+
+ return write_verbatim(p, "{\"frame_type\":\"streams_blocked\"}");
+}
+
+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"}
+ */
+#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 271
+
+ p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\",");
+ p = write_pair_number(p, "sequence_number", fr->seq);
+ *p++ = ',';
+ p = write_pair_number(p, "retire_prior_to", fr->retire_prior_to);
+ *p++ = ',';
+ 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,
+ sizeof(fr->stateless_reset_token));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *
+write_retire_connection_id_frame(uint8_t *p,
+ const ngtcp2_retire_connection_id *fr) {
+ /*
+ * {"frame_type":"retire_connection_id","sequence_number":0000000000000000000}
+ */
+#define NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD 75
+
+ p = write_verbatim(p, "{\"frame_type\":\"retire_connection_id\",");
+ p = write_pair_number(p, "sequence_number", fr->seq);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_path_challenge_frame(uint8_t *p,
+ const ngtcp2_path_challenge *fr) {
+ /*
+ * {"frame_type":"path_challenge","data":"xxxxxxxxxxxxxxxx"}
+ */
+#define NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD 57
+
+ p = write_verbatim(p, "{\"frame_type\":\"path_challenge\",");
+ p = write_pair_hex(p, "data", fr->data, sizeof(fr->data));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_path_response_frame(uint8_t *p,
+ const ngtcp2_path_response *fr) {
+ /*
+ * {"frame_type":"path_response","data":"xxxxxxxxxxxxxxxx"}
+ */
+#define NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD 56
+
+ p = write_verbatim(p, "{\"frame_type\":\"path_response\",");
+ p = write_pair_hex(p, "data", fr->data, sizeof(fr->data));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *
+write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) {
+ /*
+ * {"frame_type":"connection_close","error_space":"application","error_code":0000000000000000000,"raw_error_code":0000000000000000000}
+ */
+#define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131
+
+ p = write_verbatim(p, "{\"frame_type\":\"connection_close\",");
+ p = write_string(p, "error_space");
+ *p++ = ':';
+ if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ p = write_string(p, "transport");
+ } else {
+ p = write_string(p, "application");
+ }
+ *p++ = ',';
+ p = write_pair_number(p, "error_code", fr->error_code);
+ *p++ = ',';
+ p = write_pair_number(p, "raw_error_code", fr->error_code);
+ /* TODO Write reason by escaping non-printables */
+ /* TODO Write trigger_frame_type */
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_handshake_done_frame(uint8_t *p,
+ const ngtcp2_handshake_done *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"handshake_done"}
+ */
+#define NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD 31
+
+ return write_verbatim(p, "{\"frame_type\":\"handshake_done\"}");
+}
+
+static uint8_t *write_datagram_frame(uint8_t *p, const ngtcp2_datagram *fr) {
+ /*
+ * {"frame_type":"datagram","length":0000000000000000000}
+ */
+#define NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD 54
+
+ p = write_verbatim(p, "{\"frame_type\":\"datagram\",");
+ p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *qlog_write_time(ngtcp2_qlog *qlog, uint8_t *p) {
+ return write_pair_tstamp(p, "time", qlog->last_ts - qlog->ts);
+}
+
+static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) {
+ uint8_t *p;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ ngtcp2_buf_reset(&qlog->buf);
+ p = qlog->buf.last;
+
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(p, ",\"name\":");
+ if (sent) {
+ p = write_string(p, "transport:packet_sent");
+ } else {
+ p = write_string(p, "transport:packet_received");
+ }
+ p = write_verbatim(p, ",\"data\":{\"frames\":[");
+ qlog->buf.last = p;
+}
+
+static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen) {
+ uint8_t *p = qlog->buf.last;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ /*
+ * ],"header":,"raw":{"packet_size":0000000000000000000}}}
+ *
+ * plus, terminating LF
+ */
+#define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD \
+ (1 + 55 + NGTCP2_QLOG_PKT_HD_OVERHEAD)
+
+ assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD);
+ assert(ngtcp2_buf_len(&qlog->buf));
+
+ /* Eat last ',' */
+ if (*(p - 1) == ',') {
+ --p;
+ }
+
+ p = write_verbatim(p, "],\"header\":");
+ p = write_pkt_hd(p, hd);
+ p = write_verbatim(p, ",\"raw\":{\"packet_size\":");
+ p = write_number(p, pktlen);
+ p = write_verbatim(p, "}}}\n");
+
+ qlog->buf.last = p;
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, qlog->buf.pos,
+ ngtcp2_buf_len(&qlog->buf));
+}
+
+void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
+ uint8_t *p = qlog->buf.last;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ 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) {
+ 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) {
+ return;
+ }
+ p = write_ping_frame(p, &fr->ping);
+ break;
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD +
+ (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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ return;
+ }
+ p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked);
+ break;
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ return;
+ }
+ p = write_path_response_frame(p, &fr->path_response);
+ break;
+ 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) {
+ 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) {
+ 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) {
+ return;
+ }
+ p = write_datagram_frame(p, &fr->datagram);
+ break;
+ default:
+ assert(0);
+ }
+
+ *p++ = ',';
+
+ qlog->buf.last = p;
+}
+
+void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog) {
+ qlog_pkt_write_start(qlog, /* sent = */ 0);
+}
+
+void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen) {
+ qlog_pkt_write_end(qlog, hd, pktlen);
+}
+
+void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog) {
+ qlog_pkt_write_start(qlog, /* sent = */ 1);
+}
+
+void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen) {
+ qlog_pkt_write_end(qlog, hd, pktlen);
+}
+
+void ngtcp2_qlog_parameters_set_transport_params(
+ ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server,
+ ngtcp2_qlog_side side) {
+ uint8_t buf[1024];
+ uint8_t *p = buf;
+ const ngtcp2_preferred_addr *paddr;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(
+ p, ",\"name\":\"transport:parameters_set\",\"data\":{\"owner\":");
+
+ if (side == NGTCP2_QLOG_SIDE_LOCAL) {
+ p = write_string(p, "local");
+ } else {
+ p = write_string(p, "remote");
+ }
+
+ *p++ = ',';
+ p = write_pair_cid(p, "initial_source_connection_id", &params->initial_scid);
+ *p++ = ',';
+ if (side == (server ? NGTCP2_QLOG_SIDE_LOCAL : NGTCP2_QLOG_SIDE_REMOTE)) {
+ p = write_pair_cid(p, "original_destination_connection_id",
+ &params->original_dcid);
+ *p++ = ',';
+ }
+ if (params->retry_scid_present) {
+ p = write_pair_cid(p, "retry_source_connection_id", &params->retry_scid);
+ *p++ = ',';
+ }
+ if (params->stateless_reset_token_present) {
+ p = write_pair_hex(p, "stateless_reset_token",
+ params->stateless_reset_token,
+ sizeof(params->stateless_reset_token));
+ *p++ = ',';
+ }
+ p = write_pair_bool(p, "disable_active_migration",
+ params->disable_active_migration);
+ *p++ = ',';
+ p = write_pair_duration(p, "max_idle_timeout", params->max_idle_timeout);
+ *p++ = ',';
+ p = write_pair_number(p, "max_udp_payload_size",
+ params->max_udp_payload_size);
+ *p++ = ',';
+ p = write_pair_number(p, "ack_delay_exponent", params->ack_delay_exponent);
+ *p++ = ',';
+ p = write_pair_duration(p, "max_ack_delay", params->max_ack_delay);
+ *p++ = ',';
+ p = write_pair_number(p, "active_connection_id_limit",
+ params->active_connection_id_limit);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_data", params->initial_max_data);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_stream_data_bidi_local",
+ params->initial_max_stream_data_bidi_local);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_stream_data_bidi_remote",
+ params->initial_max_stream_data_bidi_remote);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_stream_data_uni",
+ params->initial_max_stream_data_uni);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_streams_bidi",
+ params->initial_max_streams_bidi);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_streams_uni",
+ params->initial_max_streams_uni);
+ if (params->preferred_address_present) {
+ *p++ = ',';
+ paddr = &params->preferred_address;
+ p = write_string(p, "preferred_address");
+ *p++ = ':';
+ *p++ = '{';
+ p = write_pair_hex(p, "ip_v4", paddr->ipv4_addr, sizeof(paddr->ipv4_addr));
+ *p++ = ',';
+ p = write_pair_number(p, "port_v4", paddr->ipv4_port);
+ *p++ = ',';
+ p = write_pair_hex(p, "ip_v6", paddr->ipv6_addr, sizeof(paddr->ipv6_addr));
+ *p++ = ',';
+ 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,
+ sizeof(paddr->stateless_reset_token));
+ *p++ = '}';
+ }
+ *p++ = ',';
+ p = write_pair_number(p, "max_datagram_frame_size",
+ params->max_datagram_frame_size);
+ p = write_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog,
+ const ngtcp2_conn_stat *cstat) {
+ uint8_t buf[1024];
+ uint8_t *p = buf;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{");
+
+ if (cstat->min_rtt != UINT64_MAX) {
+ p = write_pair_duration(p, "min_rtt", cstat->min_rtt);
+ *p++ = ',';
+ }
+ p = write_pair_duration(p, "smoothed_rtt", cstat->smoothed_rtt);
+ *p++ = ',';
+ p = write_pair_duration(p, "latest_rtt", cstat->latest_rtt);
+ *p++ = ',';
+ p = write_pair_duration(p, "rtt_variance", cstat->rttvar);
+ *p++ = ',';
+ p = write_pair_number(p, "pto_count", cstat->pto_count);
+ *p++ = ',';
+ p = write_pair_number(p, "congestion_window", cstat->cwnd);
+ *p++ = ',';
+ p = write_pair_number(p, "bytes_in_flight", cstat->bytes_in_flight);
+ if (cstat->ssthresh != UINT64_MAX) {
+ *p++ = ',';
+ p = write_pair_number(p, "ssthresh", cstat->ssthresh);
+ }
+
+ p = write_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) {
+ uint8_t buf[256];
+ uint8_t *p = buf;
+ ngtcp2_pkt_hd hd = {0};
+
+ if (!qlog->write) {
+ return;
+ }
+
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(
+ p, ",\"name\":\"recovery:packet_lost\",\"data\":{\"header\":");
+
+ hd.type = ent->hd.type;
+ hd.flags = ent->hd.flags;
+ hd.pkt_num = ent->hd.pkt_num;
+
+ p = write_pkt_hd(p, &hd);
+ p = write_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog,
+ const ngtcp2_pkt_hd *hd) {
+ uint8_t buf[256];
+ uint8_t *p = buf;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ *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_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h
new file mode 100644
index 0000000000..cb3c2063f7
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h
@@ -0,0 +1,144 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_QLOG_H
+#define NGTCP2_QLOG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_cc.h"
+#include "ngtcp2_buf.h"
+#include "ngtcp2_rtb.h"
+
+/* NGTCP2_QLOG_BUFLEN is the length of heap allocated buffer for
+ qlog. */
+#define NGTCP2_QLOG_BUFLEN 4096
+
+typedef enum ngtcp2_qlog_side {
+ NGTCP2_QLOG_SIDE_LOCAL,
+ NGTCP2_QLOG_SIDE_REMOTE,
+} ngtcp2_qlog_side;
+
+typedef struct ngtcp2_qlog {
+ /* write is a callback function to write qlog. */
+ ngtcp2_qlog_write write;
+ /* ts is the initial timestamp */
+ ngtcp2_tstamp ts;
+ /* last_ts is the timestamp observed last time. */
+ ngtcp2_tstamp last_ts;
+ /* buf is a heap allocated buffer to write exclusively
+ packet_received and packet_sent. */
+ ngtcp2_buf buf;
+ /* user_data is an opaque pointer which is passed to write
+ callback. */
+ void *user_data;
+} ngtcp2_qlog;
+
+/*
+ * ngtcp2_qlog_init initializes |qlog|.
+ */
+void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write,
+ ngtcp2_tstamp ts, void *user_data);
+
+/*
+ * ngtcp2_qlog_start writes qlog preamble.
+ */
+void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server);
+
+/*
+ * ngtcp2_qlog_end writes closing part of qlog.
+ */
+void ngtcp2_qlog_end(ngtcp2_qlog *qlog);
+
+/*
+ * ngtcp2_qlog_write_frame writes |fr| to qlog->buf.
+ * ngtcp2_qlog_pkt_received_start or ngtcp2_qlog_pkt_sent_start must
+ * be called before calling this function.
+ */
+void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr);
+
+/*
+ * ngtcp2_qlog_pkt_received_start starts to write packet_received
+ * event. It initializes qlog->buf. It writes qlog to qlog->buf.
+ * ngtcp2_qlog_pkt_received_end will flush the content of qlog->buf to
+ * write callback.
+ */
+void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog);
+
+/*
+ * ngtcp2_qlog_pkt_received_end ends packet_received event and sends
+ * the content of qlog->buf to qlog->write callback.
+ */
+void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen);
+
+/*
+ * ngtcp2_qlog_pkt_sent_start starts to write packet_sent event. It
+ * initializes qlog->buf. It writes qlog to qlog->buf.
+ * ngtcp2_qlog_pkt_sent_end will flush the content of qlog->buf to
+ * write callback.
+ */
+void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog);
+
+/*
+ * ngtcp2_qlog_pkt_sent_end ends packet_sent event and sends the
+ * content of qlog->buf to qlog->write callback.
+ */
+void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen);
+
+/*
+ * ngtcp2_qlog_parameters_set_transport_params writes |params| to qlog
+ * as parameters_set event. |server| is nonzero if the local endpoint
+ * is server. If |local| is nonzero, it is "owner" field becomes
+ * "local", otherwise "remote".
+ */
+void ngtcp2_qlog_parameters_set_transport_params(
+ ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server,
+ ngtcp2_qlog_side side);
+
+/*
+ * ngtcp2_qlog_metrics_updated writes metrics_updated event of
+ * recovery category.
+ */
+void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog,
+ const ngtcp2_conn_stat *cstat);
+
+/*
+ * ngtcp2_qlog_pkt_lost writes packet_lost event.
+ */
+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);
+
+#endif /* NGTCP2_QLOG_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c
new file mode 100644
index 0000000000..9379496b7d
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c
@@ -0,0 +1,61 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_range.h"
+#include "ngtcp2_macro.h"
+
+void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end) {
+ r->begin = begin;
+ r->end = end;
+}
+
+ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a,
+ const ngtcp2_range *b) {
+ ngtcp2_range r = {0, 0};
+ uint64_t begin = ngtcp2_max(a->begin, b->begin);
+ uint64_t end = ngtcp2_min(a->end, b->end);
+ if (begin < end) {
+ ngtcp2_range_init(&r, begin, end);
+ }
+ return r;
+}
+
+uint64_t ngtcp2_range_len(const ngtcp2_range *r) { return r->end - r->begin; }
+
+int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b) {
+ return a->begin == b->begin && a->end == b->end;
+}
+
+void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right,
+ const ngtcp2_range *a, const ngtcp2_range *b) {
+ /* Assume that b is included in a */
+ left->begin = a->begin;
+ left->end = b->begin;
+ right->begin = b->end;
+ right->end = a->end;
+}
+
+int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b) {
+ return a->end <= b->end;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h
new file mode 100644
index 0000000000..a776c4ec47
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h
@@ -0,0 +1,80 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RANGE_H
+#define NGTCP2_RANGE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_range represents half-closed range [begin, end).
+ */
+typedef struct ngtcp2_range {
+ uint64_t begin;
+ uint64_t end;
+} ngtcp2_range;
+
+/*
+ * ngtcp2_range_init initializes |r| with the range [|begin|, |end|).
+ */
+void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end);
+
+/*
+ * ngtcp2_range_intersect returns the intersection of |a| and |b|. If
+ * they do not overlap, it returns empty range.
+ */
+ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a,
+ const ngtcp2_range *b);
+
+/*
+ * ngtcp2_range_len returns the length of |r|.
+ */
+uint64_t ngtcp2_range_len(const ngtcp2_range *r);
+
+/*
+ * ngtcp2_range_eq returns nonzero if |a| equals |b|, such that
+ * a->begin == b->begin, and a->end == b->end hold.
+ */
+int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b);
+
+/*
+ * ngtcp2_range_cut returns the left and right range after removing
+ * |b| from |a|. This function assumes that |a| completely includes
+ * |b|. In other words, a->begin <= b->begin and b->end <= a->end
+ * hold.
+ */
+void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right,
+ const ngtcp2_range *a, const ngtcp2_range *b);
+
+/*
+ * ngtcp2_range_not_after returns nonzero if the right edge of |a|
+ * does not go beyond of the right edge of |b|.
+ */
+int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b);
+
+#endif /* NGTCP2_RANGE_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h
new file mode 100644
index 0000000000..e392c34ebf
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h
@@ -0,0 +1,42 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RCVRY_H
+#define NGTCP2_RCVRY_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in
+ draft-ietf-quic-recovery-22. */
+#define NGTCP2_PKT_THRESHOLD 3
+
+/* NGTCP2_GRANULARITY is kGranularity described in
+ draft-ietf-quic-recovery-17. */
+#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
new file mode 100644
index 0000000000..e4deab1ff7
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
@@ -0,0 +1,114 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ringbuf.h"
+
+#include <assert.h>
+#ifdef WIN32
+# include <intrin.h>
+#endif
+
+#include "ngtcp2_macro.h"
+
+#if defined(_MSC_VER) && defined(_M_ARM64)
+unsigned int __popcnt(unsigned int x) {
+ unsigned int c = 0;
+ for (; x; ++c) {
+ x &= x - 1;
+ }
+ return c;
+}
+#endif
+
+int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ 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->mem = mem;
+ rb->nmemb = nmemb;
+ rb->size = size;
+ rb->first = 0;
+ rb->len = 0;
+
+ return 0;
+}
+
+void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) {
+ if (rb == NULL) {
+ return;
+ }
+
+ ngtcp2_mem_free(rb->mem, rb->buf);
+}
+
+void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb) {
+ rb->first = (rb->first - 1) & (rb->nmemb - 1);
+ rb->len = ngtcp2_min(rb->nmemb, rb->len + 1);
+
+ return (void *)&rb->buf[rb->first * rb->size];
+}
+
+void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb) {
+ size_t offset = (rb->first + rb->len) & (rb->nmemb - 1);
+
+ if (rb->len == rb->nmemb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ } else {
+ ++rb->len;
+ }
+
+ return (void *)&rb->buf[offset * rb->size];
+}
+
+void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ --rb->len;
+}
+
+void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb) {
+ assert(rb->len);
+ --rb->len;
+}
+
+void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len) {
+ assert(len <= rb->nmemb);
+ rb->len = len;
+}
+
+void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset) {
+ assert(offset < rb->len);
+ offset = (rb->first + offset) & (rb->nmemb - 1);
+ return &rb->buf[offset * rb->size];
+}
+
+int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb) { return rb->len == rb->nmemb; }
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
new file mode 100644
index 0000000000..c6e1421518
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
@@ -0,0 +1,110 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RINGBUF_H
+#define NGTCP2_RINGBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+typedef struct ngtcp2_ringbuf {
+ /* buf points to the underlying buffer. */
+ uint8_t *buf;
+ const ngtcp2_mem *mem;
+ /* nmemb is the number of elements that can be stored in this ring
+ buffer. */
+ size_t nmemb;
+ /* size is the size of each element. */
+ size_t size;
+ /* first is the offset to the first element. */
+ size_t first;
+ /* len is the number of elements actually stored. */
+ size_t len;
+} ngtcp2_ringbuf;
+
+/*
+ * ngtcp2_ringbuf_init initializes |rb|. |nmemb| is the number of
+ * elements that can be stored in this buffer. |size| is the size of
+ * each element. |size| must be power of 2.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_ringbuf_free frees resources allocated for |rb|. This
+ * function does not free the memory pointed by |rb|.
+ */
+void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb);
+
+/* ngtcp2_ringbuf_push_front moves the offset to the first element in
+ the buffer backward, and returns the pointer to the element.
+ Caller can store data to the buffer pointed by the returned
+ pointer. If this action exceeds the capacity of the ring buffer,
+ the last element is silently overwritten, and rb->len remains
+ unchanged. */
+void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb);
+
+/* ngtcp2_ringbuf_push_back moves the offset to the last element in
+ the buffer forward, and returns the pointer to the element. Caller
+ can store data to the buffer pointed by the returned pointer. If
+ this action exceeds the capacity of the ring buffer, the first
+ element is silently overwritten, and rb->len remains unchanged. */
+void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb);
+
+/*
+ * ngtcp2_ringbuf_pop_front removes first element in |rb|.
+ */
+void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb);
+
+/*
+ * ngtcp2_ringbuf_pop_back removes the last element in |rb|.
+ */
+void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb);
+
+/* ngtcp2_ringbuf_resize changes the number of elements stored. This
+ does not change the capacity of the underlying buffer. */
+void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len);
+
+/* ngtcp2_ringbuf_get returns the pointer to the element at
+ |offset|. */
+void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset);
+
+/* ngtcp2_ringbuf_len returns the number of elements stored. */
+#define ngtcp2_ringbuf_len(RB) ((RB)->len)
+
+/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */
+int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb);
+
+#endif /* NGTCP2_RINGBUF_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c
new file mode 100644
index 0000000000..499c07ec6b
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c
@@ -0,0 +1,327 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rob.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+
+int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end,
+ const ngtcp2_mem *mem) {
+ *pg = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_gap));
+ if (*pg == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ (*pg)->range.begin = begin;
+ (*pg)->range.end = end;
+
+ return 0;
+}
+
+void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, g);
+}
+
+int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk,
+ const ngtcp2_mem *mem) {
+ *pd = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_data) + chunk);
+ if (*pd == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ (*pd)->range.begin = offset;
+ (*pd)->range.end = offset + chunk;
+ (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data);
+ (*pd)->end = (*pd)->begin + chunk;
+
+ return 0;
+}
+
+void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, d);
+}
+
+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;
+ }
+
+ rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem);
+ if (rv != 0) {
+ goto fail_rob_gap_new;
+ }
+
+ rv = ngtcp2_ksl_insert(&rob->gapksl, NULL, &g->range, g);
+ if (rv != 0) {
+ 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;
+ }
+
+ 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;
+}
+
+void ngtcp2_rob_free(ngtcp2_rob *rob) {
+ ngtcp2_ksl_it it;
+
+ if (rob == NULL) {
+ return;
+ }
+
+ for (it = ngtcp2_ksl_begin(&rob->dataksl); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_rob_data_del(ngtcp2_ksl_it_get(&it), rob->mem);
+ }
+
+ for (it = ngtcp2_ksl_begin(&rob->gapksl); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_rob_gap_del(ngtcp2_ksl_it_get(&it), rob->mem);
+ }
+
+ ngtcp2_ksl_free(&rob->dataksl);
+ ngtcp2_ksl_free(&rob->gapksl);
+}
+
+static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+ size_t len) {
+ size_t n;
+ int rv;
+ ngtcp2_rob_data *d;
+ ngtcp2_range range = {offset, offset + len};
+ ngtcp2_ksl_it it;
+
+ for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl, &range,
+ ngtcp2_ksl_range_exclusive_compar);
+ len; ngtcp2_ksl_it_next(&it)) {
+ if (ngtcp2_ksl_it_end(&it)) {
+ d = NULL;
+ } else {
+ d = ngtcp2_ksl_it_get(&it);
+ }
+
+ if (d == NULL || offset < d->range.begin) {
+ rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk,
+ rob->chunk, rob->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_ksl_insert(&rob->dataksl, &it, &d->range, d);
+ if (rv != 0) {
+ ngtcp2_rob_data_del(d, rob->mem);
+ return rv;
+ }
+ }
+
+ n = (size_t)ngtcp2_min((uint64_t)len, d->range.begin + rob->chunk - offset);
+ memcpy(d->begin + (offset - d->range.begin), data, n);
+ offset += n;
+ data += n;
+ len -= n;
+ }
+
+ return 0;
+}
+
+int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+ size_t datalen) {
+ int rv;
+ ngtcp2_rob_gap *g;
+ ngtcp2_range m, l, r, q = {offset, offset + datalen};
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ g = ngtcp2_ksl_it_get(&it);
+
+ m = ngtcp2_range_intersect(&q, &g->range);
+ if (!ngtcp2_range_len(&m)) {
+ break;
+ }
+ if (ngtcp2_range_eq(&g->range, &m)) {
+ ngtcp2_ksl_remove(&rob->gapksl, &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));
+ if (rv != 0) {
+ return rv;
+ }
+
+ continue;
+ }
+ ngtcp2_range_cut(&l, &r, &g->range, &m);
+ if (ngtcp2_range_len(&l)) {
+ ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &l);
+ g->range = l;
+
+ if (ngtcp2_range_len(&r)) {
+ ngtcp2_rob_gap *ng;
+ rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = ngtcp2_ksl_insert(&rob->gapksl, &it, &ng->range, ng);
+ if (rv != 0) {
+ ngtcp2_rob_gap_del(ng, rob->mem);
+ return rv;
+ }
+ }
+ } else if (ngtcp2_range_len(&r)) {
+ ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r);
+ g->range = r;
+ }
+ rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
+ (size_t)ngtcp2_range_len(&m));
+ if (rv != 0) {
+ return rv;
+ }
+ ngtcp2_ksl_it_next(&it);
+ }
+ return 0;
+}
+
+int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) {
+ ngtcp2_rob_gap *g;
+ ngtcp2_rob_data *d;
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_begin(&rob->gapksl);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ g = ngtcp2_ksl_it_get(&it);
+ if (offset <= g->range.begin) {
+ break;
+ }
+ if (offset < g->range.end) {
+ ngtcp2_range r = {offset, g->range.end};
+ ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r);
+ g->range.begin = offset;
+ break;
+ }
+ ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range);
+ ngtcp2_rob_gap_del(g, rob->mem);
+ }
+
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ d = ngtcp2_ksl_it_get(&it);
+ if (offset < d->range.begin + rob->chunk) {
+ return 0;
+ }
+ ngtcp2_ksl_remove(&rob->dataksl, &it, &d->range);
+ ngtcp2_rob_data_del(d, rob->mem);
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
+ uint64_t offset) {
+ ngtcp2_rob_gap *g;
+ ngtcp2_rob_data *d;
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_begin(&rob->gapksl);
+ if (ngtcp2_ksl_it_end(&it)) {
+ return 0;
+ }
+
+ g = ngtcp2_ksl_it_get(&it);
+
+ if (g->range.begin <= offset) {
+ return 0;
+ }
+
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+ d = ngtcp2_ksl_it_get(&it);
+
+ assert(d);
+ assert(d->range.begin <= offset);
+ assert(offset < d->range.begin + rob->chunk);
+
+ *pdest = d->begin + (offset - d->range.begin);
+
+ return (size_t)(ngtcp2_min(g->range.begin, d->range.begin + rob->chunk) -
+ offset);
+}
+
+void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rob_data *d;
+
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+ d = ngtcp2_ksl_it_get(&it);
+
+ assert(d);
+
+ if (offset + len < d->range.begin + rob->chunk) {
+ return;
+ }
+
+ ngtcp2_ksl_remove(&rob->dataksl, NULL, &d->range);
+ ngtcp2_rob_data_del(d, rob->mem);
+}
+
+uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob) {
+ ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rob->gapksl);
+ ngtcp2_rob_gap *g;
+
+ if (ngtcp2_ksl_it_end(&it)) {
+ return UINT64_MAX;
+ }
+
+ g = ngtcp2_ksl_it_get(&it);
+
+ return g->range.begin;
+}
+
+int ngtcp2_rob_data_buffered(ngtcp2_rob *rob) {
+ return ngtcp2_ksl_len(&rob->dataksl) != 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h
new file mode 100644
index 0000000000..c7688df454
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h
@@ -0,0 +1,197 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ROB_H
+#define NGTCP2_ROB_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_range.h"
+#include "ngtcp2_ksl.h"
+
+/*
+ * ngtcp2_rob_gap represents the gap, which is the range of stream
+ * data that is not received yet.
+ */
+typedef struct ngtcp2_rob_gap {
+ /* range is the range of this gap. */
+ ngtcp2_range range;
+} ngtcp2_rob_gap;
+
+/*
+ * ngtcp2_rob_gap_new allocates new ngtcp2_rob_gap object, and assigns
+ * its pointer to |*pg|. The caller should call ngtcp2_rob_gap_del to
+ * delete it when it is no longer used. The range of the gap is
+ * [begin, end). |mem| is custom memory allocator to allocate memory.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_gap_del deallocates |g|. It deallocates the memory
+ * pointed by |g| it self. |mem| is custom memory allocator to
+ * deallocate memory.
+ */
+void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_data holds the buffered stream data.
+ */
+typedef struct ngtcp2_rob_data {
+ /* range is the range of this gap. */
+ ngtcp2_range range;
+ /* begin points to the buffer. */
+ uint8_t *begin;
+ /* end points to the one beyond of the last byte of the buffer */
+ uint8_t *end;
+} ngtcp2_rob_data;
+
+/*
+ * ngtcp2_rob_data_new allocates new ngtcp2_rob_data object, and
+ * assigns its pointer to |*pd|. The caller should call
+ * ngtcp2_rob_data_del to delete it when it is no longer used.
+ * |offset| is the stream offset of the first byte of this data.
+ * |chunk| is the size of the buffer. |offset| must be multiple of
+ * |chunk|. |mem| is custom memory allocator to allocate memory.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_data_del deallocates |d|. It deallocates the memory
+ * pointed by |d| itself. |mem| is custom memory allocator to
+ * deallocate memory.
+ */
+void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob is the reorder buffer which reassembles stream data
+ * received in out of order.
+ */
+typedef struct ngtcp2_rob {
+ /* gapksl maintains the range of offset which is not received
+ yet. Initially, its range is [0, UINT64_MAX). */
+ ngtcp2_ksl gapksl;
+ /* dataksl maintains the list of buffers which store received data
+ ordered by stream offset. */
+ ngtcp2_ksl dataksl;
+ /* mem is custom memory allocator */
+ const ngtcp2_mem *mem;
+ /* chunk is the size of each buffer in data field */
+ size_t chunk;
+} ngtcp2_rob;
+
+/*
+ * ngtcp2_rob_init initializes |rob|. |chunk| is the size of buffer
+ * per chunk.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_free frees resources allocated for |rob|.
+ */
+void ngtcp2_rob_free(ngtcp2_rob *rob);
+
+/*
+ * ngtcp2_rob_push adds new data of length |datalen| at the stream
+ * offset |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+ size_t datalen);
+
+/*
+ * ngtcp2_rob_remove_prefix removes gap up to |offset|, exclusive. It
+ * also removes data buffer if it is completely included in |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset);
+
+/*
+ * ngtcp2_rob_data_at stores the pointer to the buffer of stream
+ * offset |offset| to |*pdest| if it is available, and returns the
+ * valid length of available data. If no data is available, it
+ * returns 0.
+ */
+size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
+ uint64_t offset);
+
+/*
+ * ngtcp2_rob_pop clears data at stream offset |offset| of length
+ * |len|.
+ *
+ * |offset| must be the offset given in ngtcp2_rob_data_at. |len|
+ * must be the return value of ngtcp2_rob_data_at when |offset| is
+ * passed.
+ *
+ * Caller should call this function from offset 0 in non-decreasing
+ * order.
+ */
+void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len);
+
+/*
+ * ngtcp2_rob_first_gap_offset returns the offset to the first gap.
+ * If there is no gap, it returns UINT64_MAX.
+ */
+uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob);
+
+/*
+ * ngtcp2_rob_data_buffered returns nonzero if any data is buffered.
+ */
+int ngtcp2_rob_data_buffered(ngtcp2_rob *rob);
+
+#endif /* NGTCP2_ROB_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c
new file mode 100644
index 0000000000..e546fdf85c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c
@@ -0,0 +1,107 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rst.h"
+#include "ngtcp2_rtb.h"
+#include "ngtcp2_cc.h"
+#include "ngtcp2_macro.h"
+
+void ngtcp2_rs_init(ngtcp2_rs *rs) {
+ rs->interval = UINT64_MAX;
+ rs->delivered = 0;
+ rs->prior_delivered = 0;
+ rs->prior_ts = 0;
+ rs->send_elapsed = 0;
+ rs->ack_elapsed = 0;
+ rs->is_app_limited = 0;
+}
+
+void ngtcp2_rst_init(ngtcp2_rst *rst) {
+ ngtcp2_rs_init(&rst->rs);
+ rst->delivered = 0;
+ rst->delivered_ts = 0;
+ rst->first_sent_ts = 0;
+ rst->app_limited = 0;
+}
+
+void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
+ const ngtcp2_conn_stat *cstat) {
+ if (cstat->bytes_in_flight == 0) {
+ rst->first_sent_ts = rst->delivered_ts = ent->ts;
+ }
+ ent->rst.first_sent_ts = rst->first_sent_ts;
+ ent->rst.delivered_ts = rst->delivered_ts;
+ ent->rst.delivered = rst->delivered;
+ ent->rst.is_app_limited = rst->app_limited != 0;
+}
+
+int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
+ ngtcp2_rs *rs = &rst->rs;
+
+ if (rst->app_limited && rst->delivered > rst->app_limited) {
+ rst->app_limited = 0;
+ }
+
+ if (rs->prior_ts == 0) {
+ return 0;
+ }
+
+ rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed);
+
+ rs->delivered = rst->delivered - rs->prior_delivered;
+
+ 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;
+ }
+
+ return 0;
+}
+
+void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
+ ngtcp2_tstamp ts) {
+ ngtcp2_rs *rs = &rst->rs;
+
+ rst->delivered += ent->pktlen;
+ rst->delivered_ts = ts;
+
+ if (ent->rst.delivered > rs->prior_delivered) {
+ rs->prior_delivered = ent->rst.delivered;
+ rs->prior_ts = ent->rst.delivered_ts;
+ 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;
+ rst->first_sent_ts = ent->ts;
+ }
+}
+
+void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
+ (void)rst;
+ (void)cstat;
+ /* TODO Not implemented */
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h
new file mode 100644
index 0000000000..14aae998eb
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h
@@ -0,0 +1,74 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RST_H
+#define NGTCP2_RST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
+
+/**
+ * @struct
+ *
+ * ngtcp2_rs contains connection state for delivery rate estimation.
+ */
+typedef struct ngtcp2_rs {
+ ngtcp2_duration interval;
+ uint64_t delivered;
+ uint64_t prior_delivered;
+ ngtcp2_tstamp prior_ts;
+ ngtcp2_duration send_elapsed;
+ ngtcp2_duration ack_elapsed;
+ int is_app_limited;
+} ngtcp2_rs;
+
+void ngtcp2_rs_init(ngtcp2_rs *rs);
+
+/*
+ * ngtcp2_rst implements delivery rate estimation described in
+ * https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00
+ */
+typedef struct ngtcp2_rst {
+ ngtcp2_rs rs;
+ uint64_t delivered;
+ ngtcp2_tstamp delivered_ts;
+ ngtcp2_tstamp first_sent_ts;
+ uint64_t app_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);
+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);
+
+#endif /* NGTCP2_RST_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c
new file mode 100644
index 0000000000..1ef67ccbc6
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c
@@ -0,0 +1,1301 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rtb.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_conn.h"
+#include "ngtcp2_log.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_cc.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_rst.h"
+
+int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) {
+ *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain));
+ 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);
+ if (*pfrc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_frame_chain_init(*pfrc);
+
+ 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);
+
+ if (datacnt > 0 && need > avail) {
+ return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ }
+
+ return ngtcp2_frame_chain_new(pfrc, mem);
+}
+
+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);
+
+ if (datacnt > 0 && need > avail) {
+ return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ }
+
+ return ngtcp2_frame_chain_new(pfrc, mem);
+}
+
+int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
+ const ngtcp2_vec *token,
+ const ngtcp2_mem *mem) {
+ size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token);
+ int rv;
+ uint8_t *p;
+ ngtcp2_frame *fr;
+
+ if (token->len > avail) {
+ rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem);
+ } else {
+ rv = ngtcp2_frame_chain_new(pfrc, mem);
+ }
+ if (rv != 0) {
+ return rv;
+ }
+
+ fr = &(*pfrc)->fr;
+ fr->type = NGTCP2_FRAME_NEW_TOKEN;
+
+ p = (uint8_t *)(*pfrc) + sizeof(ngtcp2_new_token);
+ memcpy(p, token->base, token->len);
+
+ ngtcp2_vec_init(&fr->new_token.token, p, token->len);
+
+ return 0;
+}
+
+void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_binder *binder;
+
+ if (frc == NULL) {
+ return;
+ }
+
+ binder = frc->binder;
+ if (binder && --binder->refcount == 0) {
+ ngtcp2_mem_free(mem, binder);
+ }
+
+ ngtcp2_mem_free(mem, 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) {
+ ngtcp2_frame_chain *next;
+
+ for (; frc;) {
+ next = frc->next;
+ ngtcp2_frame_chain_del(frc, mem);
+ frc = next;
+ }
+}
+
+int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder,
+ const ngtcp2_mem *mem) {
+ *pbinder = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_frame_chain_binder));
+ if (*pbinder == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ return 0;
+}
+
+int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_binder *binder;
+ int rv;
+
+ assert(b->binder == NULL);
+
+ if (a->binder == NULL) {
+ rv = ngtcp2_frame_chain_binder_new(&binder, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ a->binder = binder;
+ ++a->binder->refcount;
+ }
+
+ b->binder = a->binder;
+ ++b->binder->refcount;
+
+ 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));
+ 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;
+
+ return 0;
+}
+
+void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) {
+ if (ent == NULL) {
+ return;
+ }
+
+ ngtcp2_frame_chain_list_del(ent->frc, mem);
+
+ ngtcp2_mem_free(mem, ent);
+}
+
+static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs > *(int64_t *)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_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem);
+ rtb->crypto = crypto;
+ rtb->rst = rst;
+ rtb->cc = cc;
+ rtb->log = log;
+ rtb->qlog = qlog;
+ rtb->mem = mem;
+ rtb->largest_acked_tx_pkt_num = -1;
+ rtb->num_ack_eliciting = 0;
+ rtb->num_retransmittable = 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;
+}
+
+void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
+ ngtcp2_ksl_it it;
+
+ if (rtb == NULL) {
+ return;
+ }
+
+ 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_ksl_free(&rtb->ents);
+}
+
+static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat) {
+ ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat);
+
+ assert(rtb->cc_pkt_num <= ent->hd.pkt_num);
+
+ cstat->bytes_in_flight += ent->pktlen;
+ rtb->cc_bytes_in_flight += ent->pktlen;
+
+ ngtcp2_rst_update_app_limited(rtb->rst, cstat);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ ++rtb->num_ack_eliciting;
+ }
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
+ ++rtb->num_retransmittable;
+ }
+}
+
+static void 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_ACK_ELICITING) {
+ assert(rtb->num_ack_eliciting);
+ --rtb->num_ack_eliciting;
+ }
+
+ if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) &&
+ !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) {
+ assert(rtb->num_retransmittable);
+ --rtb->num_retransmittable;
+ }
+
+ 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;
+ }
+}
+
+/*
+ * 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.
+ */
+static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq;
+ ngtcp2_frame *fr;
+ ngtcp2_strm *strm;
+ ngtcp2_range gap, range;
+ size_t num_reclaimed = 0;
+ int rv;
+
+ /* 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;
+ /* Check that a late ACK acknowledged this frame. */
+ if (frc->binder &&
+ (frc->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) {
+ continue;
+ }
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ strm = ngtcp2_conn_find_stream(conn, fr->stream.stream_id);
+ if (strm == NULL) {
+ continue;
+ }
+
+ gap = ngtcp2_strm_get_unacked_range_after(strm, fr->stream.offset);
+
+ range.begin = fr->stream.offset;
+ 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;
+ }
+
+ rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt,
+ rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr = *fr;
+ ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data,
+ fr->stream.datacnt);
+
+ rv = ngtcp2_strm_streamfrq_push(strm, nfrc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(nfrc, conn->mem);
+ return rv;
+ }
+ if (!ngtcp2_strm_is_tx_queued(strm)) {
+ strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn);
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ ++num_reclaimed;
+
+ continue;
+ case NGTCP2_FRAME_CRYPTO:
+ /* Don't resend CRYPTO frame if the whole region it contains has
+ been acknowledged */
+ gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->crypto.offset);
+
+ range.begin = fr->crypto.offset;
+ range.end = fr->crypto.offset +
+ ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt);
+ range = ngtcp2_range_intersect(&range, &gap);
+ if (ngtcp2_range_len(&range) == 0) {
+ continue;
+ }
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt,
+ rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr = *fr;
+ ngtcp2_vec_copy(nfrc->fr.crypto.data, fr->crypto.data,
+ fr->crypto.datacnt);
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL,
+ &nfrc->fr.crypto.offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(nfrc, conn->mem);
+ return rv;
+ }
+
+ ++num_reclaimed;
+
+ continue;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token,
+ rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ break;
+ default:
+ rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr = *fr;
+
+ rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ break;
+ }
+
+ ++num_reclaimed;
+
+ nfrc->next = *pfrc;
+ *pfrc = nfrc;
+ pfrc = &nfrc->next;
+ }
+
+ return (ngtcp2_ssize)num_reclaimed;
+}
+
+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) {
+ int rv;
+ ngtcp2_ssize reclaimed;
+
+ ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
+ ent->ts);
+
+ if (rtb->qlog) {
+ 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;
+
+ ngtcp2_ksl_it_next(it);
+
+ return 0;
+ }
+
+ if (ent->frc) {
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+ assert(UINT64_MAX == ent->lost_ts);
+
+ reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
+ if (reclaimed < 0) {
+ return (int)reclaimed;
+ }
+
+ if (reclaimed) {
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = ts;
+
+ ++rtb->num_lost_pkts;
+
+ ngtcp2_ksl_it_next(it);
+
+ return 0;
+ }
+ }
+ } 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);
+
+ ngtcp2_rtb_entry_del(ent, rtb->mem);
+
+ return 0;
+}
+
+int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat) {
+ int rv;
+
+ rv = ngtcp2_ksl_insert(&rtb->ents, NULL, &ent->hd.pkt_num, ent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rtb_on_add(rtb, ent, cstat);
+
+ return 0;
+}
+
+ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb) {
+ return ngtcp2_ksl_begin(&rtb->ents);
+}
+
+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);
+ assert(0 == rv);
+ rtb_on_remove(rtb, ent, cstat);
+
+ assert(ent->next == NULL);
+
+ ngtcp2_list_insert(ent, pent);
+}
+
+static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn *conn) {
+ ngtcp2_frame_chain *frc;
+ uint64_t prev_stream_offset, stream_offset;
+ ngtcp2_strm *strm;
+ int rv;
+ uint64_t datalen;
+ ngtcp2_strm *crypto = rtb->crypto;
+ ngtcp2_crypto_level crypto_level;
+
+ for (frc = ent->frc; frc; frc = frc->next) {
+ if (frc->binder) {
+ frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK;
+ }
+
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id);
+ if (strm == NULL) {
+ break;
+ }
+
+ if (frc->fr.stream.fin) {
+ strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED;
+ }
+
+ prev_stream_offset = ngtcp2_strm_get_acked_offset(strm);
+ rv = ngtcp2_strm_ack_data(
+ strm, frc->fr.stream.offset,
+ ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt));
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->callbacks.acked_stream_data_offset) {
+ stream_offset = ngtcp2_strm_get_acked_offset(strm);
+ datalen = stream_offset - prev_stream_offset;
+ if (datalen == 0 && !frc->fr.stream.fin) {
+ break;
+ }
+
+ rv = conn->callbacks.acked_stream_data_offset(
+ conn, strm->stream_id, prev_stream_offset, datalen, conn->user_data,
+ strm->stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ prev_stream_offset = ngtcp2_strm_get_acked_offset(crypto);
+ rv = ngtcp2_strm_ack_data(
+ crypto, frc->fr.crypto.offset,
+ ngtcp2_vec_len(frc->fr.crypto.data, frc->fr.crypto.datacnt));
+ if (rv != 0) {
+ 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);
+ }
+
+ rv = conn->callbacks.acked_crypto_offset(
+ conn, crypto_level, prev_stream_offset, datalen, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ break;
+ case NGTCP2_FRAME_RESET_STREAM:
+ strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id);
+ if (strm == NULL) {
+ break;
+ }
+ strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED;
+ rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ assert(conn->dcid.num_retire_queued);
+ --conn->dcid.num_retire_queued;
+ break;
+ }
+ }
+ return 0;
+}
+
+static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
+ ngtcp2_cc *cc = rtb->cc;
+ ngtcp2_cc_pkt pkt;
+
+ ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts);
+
+ cc->on_pkt_acked(cc, cstat,
+ ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
+ rtb->pktns_id, ent->ts),
+ ts);
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ cstat->pto_count = 0;
+ }
+}
+
+static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_ack *fr, size_t ecn_acked,
+ ngtcp2_tstamp largest_acked_sent_ts,
+ ngtcp2_tstamp ts) {
+ if (conn->tx.ecn.state == NGTCP2_ECN_STATE_FAILED) {
+ return;
+ }
+
+ if ((ecn_acked && fr->type == NGTCP2_FRAME_ACK) ||
+ (fr->type == NGTCP2_FRAME_ACK_ECN &&
+ (pktns->rx.ecn.ack.ect0 > fr->ecn.ect0 ||
+ pktns->rx.ecn.ack.ect1 > fr->ecn.ect1 ||
+ pktns->rx.ecn.ack.ce > fr->ecn.ce ||
+ (fr->ecn.ect0 - pktns->rx.ecn.ack.ect0) +
+ (fr->ecn.ce - pktns->rx.ecn.ack.ce) <
+ ecn_acked ||
+ fr->ecn.ect0 > pktns->tx.ecn.ect0 || fr->ecn.ect1))) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "path is not ECN capable");
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED;
+ return;
+ }
+
+ if (conn->tx.ecn.state != NGTCP2_ECN_STATE_CAPABLE && ecn_acked) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "path is ECN capable");
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_CAPABLE;
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ if (largest_acked_sent_ts != UINT64_MAX &&
+ fr->ecn.ce > pktns->rx.ecn.ack.ce) {
+ cc->congestion_event(cc, cstat, largest_acked_sent_ts, ts);
+ }
+
+ pktns->rx.ecn.ack.ect0 = fr->ecn.ect0;
+ pktns->rx.ecn.ack.ect1 = fr->ecn.ect1;
+ pktns->rx.ecn.ack.ce = fr->ecn.ce;
+ }
+}
+
+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,
+ ngtcp2_tstamp ts) {
+ ngtcp2_rtb_entry *ent;
+ int64_t largest_ack = fr->largest_ack, min_ack;
+ size_t i;
+ int rv;
+ ngtcp2_ksl_it it;
+ ngtcp2_ssize num_acked = 0;
+ ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX;
+ ngtcp2_tstamp largest_acked_sent_ts = UINT64_MAX;
+ int64_t pkt_num;
+ ngtcp2_cc *cc = rtb->cc;
+ ngtcp2_rtb_entry *acked_ent = NULL;
+ int ack_eliciting_pkt_acked = 0;
+ size_t ecn_acked = 0;
+ int verify_ecn = 0;
+
+ if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) &&
+ largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) {
+ conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+ conn->crypto.key_update.confirmed_ts = ts;
+
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
+ }
+
+ if (rtb->largest_acked_tx_pkt_num < largest_ack) {
+ rtb->largest_acked_tx_pkt_num = largest_ack;
+ verify_ecn = 1;
+ }
+
+ /* 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) {
+ conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
+ largest_acked_sent_ts, ts);
+ }
+ return 0;
+ }
+
+ min_ack = largest_ack - (int64_t)fr->first_ack_blklen;
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it);
+
+ assert(pkt_num <= largest_ack);
+
+ if (pkt_num < min_ack) {
+ break;
+ }
+
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (largest_ack == pkt_num) {
+ largest_pkt_sent_ts = ent->ts;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ ack_eliciting_pkt_acked = 1;
+ }
+
+ rtb_remove(rtb, &it, &acked_ent, ent, cstat);
+ ++num_acked;
+ }
+
+ for (i = 0; i < fr->num_blks;) {
+ largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2;
+ min_ack = largest_ack - (int64_t)fr->blks[i].blklen;
+
+ it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack);
+ if (ngtcp2_ksl_it_end(&it)) {
+ break;
+ }
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it);
+ if (pkt_num < min_ack) {
+ break;
+ }
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ ack_eliciting_pkt_acked = 1;
+ }
+
+ rtb_remove(rtb, &it, &acked_ent, ent, cstat);
+ ++num_acked;
+ }
+
+ ++i;
+ }
+
+ 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->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 &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) {
+ ++ecn_acked;
+ }
+
+ assert(largest_acked_sent_ts == UINT64_MAX ||
+ largest_acked_sent_ts <= ent->ts);
+
+ largest_acked_sent_ts = ent->ts;
+
+ rv = rtb_process_acked_pkt(rtb, ent, conn);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ rtb_on_pkt_acked(rtb, ent, cstat, ts);
+ acked_ent = ent->next;
+ ngtcp2_rtb_entry_del(ent, rtb->mem);
+ }
+
+ if (verify_ecn) {
+ conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
+ largest_acked_sent_ts, ts);
+ }
+ } else {
+ /* For unit tests */
+ 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);
+ }
+ }
+
+ cc->on_ack_recv(cc, cstat, ts);
+
+ return num_acked;
+
+fail:
+ for (ent = acked_ent; ent; ent = acked_ent) {
+ acked_ent = ent->next;
+ ngtcp2_rtb_entry_del(ent, 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) {
+ ngtcp2_tstamp loss_time;
+
+ if (ent->ts <= lost_send_time ||
+ rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) {
+ return 1;
+ }
+
+ loss_time = cstat->loss_time[rtb->pktns_id];
+
+ if (loss_time == UINT64_MAX) {
+ loss_time = ent->ts + loss_delay;
+ } else {
+ loss_time = ngtcp2_min(loss_time, ent->ts + loss_delay);
+ }
+
+ cstat->loss_time[rtb->pktns_id] = loss_time;
+
+ return 0;
+}
+
+/*
+ * rtb_compute_pkt_loss_delay computes loss delay.
+ */
+static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_conn_stat *cstat) {
+ /* 9/8 is kTimeThreshold */
+ ngtcp2_duration loss_delay =
+ ngtcp2_max(cstat->latest_rtt, cstat->smoothed_rtt) * 9 / 8;
+ return ngtcp2_max(loss_delay, NGTCP2_GRANULARITY);
+}
+
+/*
+ * conn_all_ecn_pkt_lost returns nonzero if all ECN QUIC packets are
+ * lost during validation period.
+ */
+static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ return (!in_pktns || in_pktns->tx.ecn.validation_pkt_sent ==
+ in_pktns->tx.ecn.validation_pkt_lost) &&
+ (!hs_pktns || hs_pktns->tx.ecn.validation_pkt_sent ==
+ hs_pktns->tx.ecn.validation_pkt_lost) &&
+ 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) {
+ 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;
+ ngtcp2_duration loss_window, congestion_period;
+ ngtcp2_cc *cc = rtb->cc;
+ int rv;
+ uint64_t pkt_thres =
+ rtb->cc_bytes_in_flight / cstat->max_udp_payload_size / 2;
+ size_t ecn_pkt_lost = 0;
+ ngtcp2_tstamp start_ts;
+
+ pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD);
+ 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)) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
+ break;
+ }
+
+ if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, lost_send_time, pkt_thres)) {
+ /* All entries from ent are considered to be lost. */
+ latest_ts = oldest_ts = ent->ts;
+ last_lost_pkt_num = ent->hd.pkt_num;
+
+ congestion_period = (cstat->smoothed_rtt +
+ ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) +
+ conn->remote.transport_params.max_ack_delay) *
+ NGTCP2_PERSISTENT_CONGESTION_THRESHOLD;
+
+ start_ts = ngtcp2_max(rtb->persistent_congestion_start_ts,
+ cstat->first_rtt_sample_ts);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (last_lost_pkt_num == ent->hd.pkt_num + 1 && ent->ts >= start_ts) {
+ last_lost_pkt_num = ent->hd.pkt_num;
+ oldest_ts = ent->ts;
+ } else {
+ last_lost_pkt_num = -1;
+ }
+
+ if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) {
+ if (rtb->pktns_id != NGTCP2_PKTNS_ID_APPLICATION ||
+ last_lost_pkt_num == -1 ||
+ latest_ts - oldest_ts >= congestion_period) {
+ break;
+ }
+ ngtcp2_ksl_it_next(&it);
+ continue;
+ }
+
+ if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) {
+ ++ecn_pkt_lost;
+ }
+
+ rtb_on_remove(rtb, ent, cstat);
+ rv = rtb_on_pkt_lost(rtb, &it, ent, conn, pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ switch (conn->tx.ecn.state) {
+ case NGTCP2_ECN_STATE_TESTING:
+ if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
+ break;
+ }
+ if (ts - conn->tx.ecn.validation_start_ts < 3 * pto) {
+ pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost;
+ assert(pktns->tx.ecn.validation_pkt_sent >=
+ pktns->tx.ecn.validation_pkt_lost);
+ break;
+ }
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+ /* fall through */
+ case NGTCP2_ECN_STATE_UNKNOWN:
+ pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost;
+ assert(pktns->tx.ecn.validation_pkt_sent >=
+ pktns->tx.ecn.validation_pkt_lost);
+ if (conn_all_ecn_pkt_lost(conn)) {
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED;
+ }
+ break;
+ default:
+ break;
+ }
+
+ cc->congestion_event(cc, cstat, latest_ts, ts);
+
+ loss_window = latest_ts - oldest_ts;
+ /* Persistent congestion situation is only evaluated for app
+ * packet number space and for the packets sent after handshake
+ * is confirmed. During handshake, there is not much packets
+ * sent and also people seem to do lots of effort not to trigger
+ * persistent congestion there, then it is a lot easier to just
+ * not enable it during handshake.
+ */
+ if (rtb->pktns_id == NGTCP2_PKTNS_ID_APPLICATION && loss_window > 0) {
+ if (loss_window >= congestion_period) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "persistent congestion loss_window=%" PRIu64
+ " congestion_period=%" PRIu64,
+ loss_window, congestion_period);
+
+ /* Reset min_rtt, srtt, and rttvar here. Next new RTT
+ sample will be used to recalculate these values. */
+ cstat->min_rtt = UINT64_MAX;
+ cstat->smoothed_rtt = conn->local.settings.initial_rtt;
+ cstat->rttvar = conn->local.settings.initial_rtt / 2;
+ cstat->first_rtt_sample_ts = UINT64_MAX;
+
+ cc->on_persistent_congestion(cc, cstat, ts);
+ }
+ }
+
+ break;
+ }
+ }
+
+ ngtcp2_rtb_remove_excessive_lost_pkt(rtb, pkt_thres);
+
+ return 0;
+}
+
+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;
+
+ for (; rtb->num_lost_pkts > n;) {
+ assert(ngtcp2_ksl_it_end(&it));
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED);
+
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
+
+ --rtb->num_lost_pkts;
+ rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num);
+ assert(0 == rv);
+ ngtcp2_rtb_entry_del(ent, rtb->mem);
+ }
+}
+
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+ int rv;
+
+ if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+ return;
+ }
+
+ it = ngtcp2_ksl_end(&rtb->ents);
+
+ for (;;) {
+ assert(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) ||
+ ts - ent->lost_ts < pto) {
+ return;
+ }
+
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
+
+ --rtb->num_lost_pkts;
+ rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num);
+ assert(0 == rv);
+ ngtcp2_rtb_entry_del(ent, rtb->mem);
+
+ if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+ return;
+ }
+ }
+}
+
+ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+
+ if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+ return UINT64_MAX;
+ }
+
+ it = ngtcp2_ksl_end(&rtb->ents);
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) {
+ return UINT64_MAX;
+ }
+
+ return ent->lost_ts;
+}
+
+static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_frame_chain **pfrc, *frc;
+ ngtcp2_stream *sfr;
+ ngtcp2_strm *strm;
+ int rv;
+
+ ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
+ ent->ts);
+
+ if (rtb->qlog) {
+ ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64
+ " is a probe packet, no retransmission is necessary",
+ ent->hd.pkt_num);
+ 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));
+ return 0;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
+ --rtb->num_lost_pkts;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64
+ " was declared lost and has already been retransmitted",
+ ent->hd.pkt_num);
+ return 0;
+ }
+
+ 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);
+ return 0;
+ }
+
+ pfrc = &ent->frc;
+
+ for (; *pfrc;) {
+ switch ((*pfrc)->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+ sfr = &frc->fr.stream;
+
+ strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
+ if (!strm) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ break;
+ }
+ rv = ngtcp2_strm_streamfrq_push(strm, frc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+ if (!ngtcp2_strm_is_tx_queued(strm)) {
+ strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn);
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL,
+ &frc->fr.crypto.offset, frc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, conn->mem);
+ return rv;
+ }
+ break;
+ default:
+ pfrc = &(*pfrc)->next;
+ }
+ }
+
+ *pfrc = pktns->tx.frq;
+ pktns->tx.frq = ent->frc;
+ ent->frc = NULL;
+
+ return 0;
+}
+
+int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) {
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ksl_it it;
+ int rv;
+
+ it = ngtcp2_ksl_begin(&rtb->ents);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ rtb_on_remove(rtb, ent, cstat);
+ rv = ngtcp2_ksl_remove(&rtb->ents, &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);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) {
+ return ngtcp2_ksl_len(&rtb->ents) == 0;
+}
+
+void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num) {
+ rtb->cc_pkt_num = cc_pkt_num;
+ rtb->cc_bytes_in_flight = 0;
+}
+
+ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, size_t num_pkts) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ssize reclaimed;
+ size_t atmost = num_pkts;
+
+ it = ngtcp2_ksl_end(&rtb->ents);
+ for (; !ngtcp2_ksl_it_begin(&it) && num_pkts >= 1;) {
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if ((ent->flags & (NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) ||
+ !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE)) {
+ continue;
+ }
+
+ assert(ent->frc);
+
+ reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
+ if (reclaimed < 0) {
+ return reclaimed;
+ }
+
+ /* Mark reclaimed even if reclaimed == 0 so that we can skip it in
+ the next run. */
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED;
+
+ assert(rtb->num_retransmittable);
+ --rtb->num_retransmittable;
+
+ if (reclaimed) {
+ --num_pkts;
+ }
+ }
+
+ return (ngtcp2_ssize)(atmost - num_pkts);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h
new file mode 100644
index 0000000000..70f43ffd92
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h
@@ -0,0 +1,402 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RTB_H
+#define NGTCP2_RTB_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_pq.h"
+
+typedef struct ngtcp2_conn ngtcp2_conn;
+typedef struct ngtcp2_pktns ngtcp2_pktns;
+typedef struct ngtcp2_log ngtcp2_log;
+typedef struct ngtcp2_qlog ngtcp2_qlog;
+typedef struct ngtcp2_strm ngtcp2_strm;
+typedef struct ngtcp2_rst ngtcp2_rst;
+
+/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is
+ set. */
+#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00
+/* 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
+
+/*
+ * ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to
+ * share the acknowledgement state. In general, all
+ * ngtcp2_frame_chains bound to the same binder must have the same
+ * information.
+ */
+typedef struct ngtcp2_frame_chain_binder {
+ size_t refcount;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_FRAME_CHAIN_BINDER_FLAG_*. */
+ uint32_t flags;
+} ngtcp2_frame_chain_binder;
+
+int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder,
+ const ngtcp2_mem *mem);
+
+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;
+};
+
+/*
+ * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using
+ * new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL
+ * a->binder. |b| must not have non-NULL b->binder.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
+ const ngtcp2_mem *mem);
+
+/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that
+ a ngtcp2_stream can include. */
+#define NGTCP2_MAX_STREAM_DATACNT 256
+
+/* NGTCP2_MAX_CRYPTO_DATACNT is the maximum number of ngtcp2_vec that
+ a ngtcp2_crypto can include. */
+#define NGTCP2_MAX_CRYPTO_DATACNT 8
+
+/*
+ * ngtcp2_frame_chain_new allocates ngtcp2_frame_chain object and
+ * assigns its pointer to |*pfrc|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new,
+ * but it allocates extra memory |extralen| in order to extend
+ * ngtcp2_frame.
+ */
+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_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.
+ */
+int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_crypto_datacnt_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.
+ */
+int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ const ngtcp2_mem *mem);
+
+int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
+ const ngtcp2_vec *token,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the
+ * memory pointed by |frc|.
+ */
+void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, 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.
+ */
+void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc,
+ const ngtcp2_mem *mem);
+
+/* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00
+/* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a
+ probe packet. */
+#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01
+/* 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 indicates that the entry
+ elicits acknowledgement. */
+#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04
+/* 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
+/* 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
+/* 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
+
+typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
+
+/*
+ * ngtcp2_rtb_entry is an object stored in ngtcp2_rtb. It corresponds
+ * 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;
+};
+
+/*
+ * 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.
+ */
+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);
+
+/*
+ * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory
+ * pointed by |ent|.
+ */
+void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rtb tracks sent packets, and its ACK timeout for
+ * retransmission.
+ */
+typedef struct ngtcp2_rtb {
+ /* ents includes ngtcp2_rtb_entry sorted by decreasing order of
+ packet number. */
+ ngtcp2_ksl ents;
+ /* crypto is CRYPTO stream. */
+ ngtcp2_strm *crypto;
+ ngtcp2_rst *rst;
+ ngtcp2_cc *cc;
+ ngtcp2_log *log;
+ ngtcp2_qlog *qlog;
+ const ngtcp2_mem *mem;
+ /* largest_acked_tx_pkt_num is the largest packet number
+ acknowledged by the peer. */
+ int64_t largest_acked_tx_pkt_num;
+ /* num_ack_eliciting is the number of ACK eliciting entries. */
+ size_t num_ack_eliciting;
+ /* num_retransmittable is the number of packets which contain frames
+ that must be retransmitted on loss. */
+ size_t num_retransmittable;
+ /* 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. */
+ ngtcp2_pktns_id pktns_id;
+ /* cc_pkt_num is the smallest packet number that is contributed to
+ ngtcp2_conn_stat.bytes_in_flight. */
+ int64_t cc_pkt_num;
+ /* cc_bytes_in_flight is the number of in-flight bytes that is
+ contributed to ngtcp2_conn_stat.bytes_in_flight. It only
+ includes the bytes after congestion state is reset. */
+ uint64_t cc_bytes_in_flight;
+ /* persistent_congestion_start_ts is the time when persistent
+ congestion evaluation is started. It happens roughly after
+ handshake is confirmed. */
+ ngtcp2_tstamp persistent_congestion_start_ts;
+ /* num_lost_pkts is the number entries in ents which has
+ NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */
+ size_t num_lost_pkts;
+} ngtcp2_rtb;
+
+/*
+ * ngtcp2_rtb_init initializes |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_rtb_free deallocates resources allocated for |rtb|.
+ */
+void ngtcp2_rtb_free(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_add adds |ent| to |rtb|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat);
+
+/*
+ * ngtcp2_rtb_head returns the iterator which points to the entry
+ * which has the largest packet number. If there is no entry,
+ * returned value satisfies ngtcp2_ksl_it_end(&it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_recv_ack removes acked ngtcp2_rtb_entry from |rtb|.
+ * |pkt_num| is a packet number which includes |fr|. |pkt_ts| is the
+ * timestamp when packet is received. |ts| should be the current
+ * time. Usually they are the same, but for buffered packets,
+ * |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns the number of newly acknowledged packets if
+ * it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+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,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_rtb_detect_lost_pkt detects lost packets and prepends the
+ * frames contained them to |*pfrc|. Even when this function fails,
+ * some frames might be prepended to |*pfrc| and the caller should
+ * handle them.
+ */
+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_rtb_remove_expired_lost_pkt removes expired lost packet.
+ */
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_rtb_lost_pkt_ts returns the earliest time when the still
+ * retained packet was lost. It returns UINT64_MAX if no such packet
+ * exists.
+ */
+ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_remove_all removes all packets from |rtb| and prepends
+ * all frames to |*pfrc|. Even when this function fails, some frames
+ * might be prepended to |*pfrc| and the caller should handle them.
+ */
+int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat);
+
+/*
+ * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry.
+ */
+int ngtcp2_rtb_empty(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_reset_cc_state resets congestion state in |rtb|.
+ * |cc_pkt_num| is the next outbound packet number which is sent under
+ * new congestion state.
+ */
+void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num);
+
+/*
+ * ngtcp2_rtb_remove_expired_lost_pkt ensures that the number of lost
+ * packets at most |n|.
+ */
+void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n);
+
+/*
+ * ngtcp2_rtb_reclaim_on_pto reclaims up to |num_pkts| packets which
+ * are in-flight and not marked lost to send them in PTO probe. The
+ * reclaimed frames are chained to |*pfrc|.
+ *
+ * This function returns the number of packets reclaimed if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, size_t num_pkts);
+
+#endif /* NGTCP2_RTB_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c
new file mode 100644
index 0000000000..3118955b24
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c
@@ -0,0 +1,242 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_str.h"
+
+#include <string.h>
+
+#include "ngtcp2_macro.h"
+
+void *ngtcp2_cpymem(void *dest, const void *src, size_t n) {
+ memcpy(dest, src, n);
+ return (uint8_t *)dest + n;
+}
+
+uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n) {
+ memset(dest, b, n);
+ return dest + n;
+}
+
+#define LOWER_XDIGITS "0123456789abcdef"
+
+uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len) {
+ size_t i;
+ uint8_t *p = dest;
+
+ for (i = 0; i < len; ++i) {
+ *p++ = (uint8_t)LOWER_XDIGITS[data[i] >> 4];
+ *p++ = (uint8_t)LOWER_XDIGITS[data[i] & 0xf];
+ }
+
+ *p = '\0';
+
+ return dest;
+}
+
+char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
+ size_t len) {
+ size_t i;
+ char *p = dest;
+ uint8_t c;
+
+ for (i = 0; i < len; ++i) {
+ c = data[i];
+ if (0x20 <= c && c <= 0x7e) {
+ *p++ = (char)c;
+ } else {
+ *p++ = '.';
+ }
+ }
+
+ *p = '\0';
+
+ return dest;
+}
+
+/*
+ * write_uint writes |n| to the buffer pointed by |p| in decimal
+ * representation. It returns |p| plus the number of bytes written.
+ * The function assumes that the buffer has enough capacity to contain
+ * a string.
+ */
+static uint8_t *write_uint(uint8_t *p, uint64_t n) {
+ size_t nlen = 0;
+ uint64_t t;
+ uint8_t *res;
+
+ if (n == 0) {
+ *p++ = '0';
+ return p;
+ }
+ for (t = n; t; t /= 10, ++nlen)
+ ;
+ p += nlen;
+ res = p;
+ for (; n; n /= 10) {
+ *--p = (uint8_t)((n % 10) + '0');
+ }
+ return res;
+}
+
+uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr) {
+ size_t i;
+ uint8_t *p = dest;
+
+ p = write_uint(p, addr[0]);
+
+ for (i = 1; i < 4; ++i) {
+ *p++ = '.';
+ p = write_uint(p, addr[i]);
+ }
+
+ *p = '\0';
+
+ return dest;
+}
+
+/*
+ * write_hex_zsup writes the content of buffer pointed by |data| of
+ * length |len| to |dest| in hex string. Any leading zeros are
+ * suppressed. It returns |dest| plus the number of bytes written.
+ */
+static uint8_t *write_hex_zsup(uint8_t *dest, const uint8_t *data, size_t len) {
+ size_t i;
+ uint8_t *p = dest;
+ uint8_t d;
+
+ for (i = 0; i < len; ++i) {
+ d = data[i];
+ if (d >> 4) {
+ break;
+ }
+
+ d &= 0xf;
+
+ if (d) {
+ *p++ = (uint8_t)LOWER_XDIGITS[d];
+ ++i;
+ break;
+ }
+ }
+
+ if (p == dest && i == len) {
+ *p++ = '0';
+ return p;
+ }
+
+ for (; i < len; ++i) {
+ d = data[i];
+ *p++ = (uint8_t)LOWER_XDIGITS[d >> 4];
+ *p++ = (uint8_t)LOWER_XDIGITS[d & 0xf];
+ }
+
+ return p;
+}
+
+uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) {
+ uint16_t blks[8];
+ size_t i;
+ size_t zlen, zoff;
+ size_t max_zlen = 0, max_zoff = 8;
+ uint8_t *p = dest;
+
+ for (i = 0; i < 16; i += sizeof(uint16_t)) {
+ /* Copy in network byte order. */
+ memcpy(&blks[i / sizeof(uint16_t)], addr + i, sizeof(uint16_t));
+ }
+
+ for (i = 0; i < 8;) {
+ if (blks[i]) {
+ ++i;
+ continue;
+ }
+
+ zlen = 1;
+ zoff = i;
+
+ ++i;
+ for (; i < 8 && blks[i] == 0; ++i, ++zlen)
+ ;
+ if (zlen > max_zlen) {
+ max_zlen = zlen;
+ max_zoff = zoff;
+ }
+ }
+
+ /* Do not suppress a single '0' block */
+ if (max_zlen == 1) {
+ max_zoff = 8;
+ }
+
+ if (max_zoff != 0) {
+ p = write_hex_zsup(p, (const uint8_t *)blks, sizeof(uint16_t));
+
+ for (i = 1; i < max_zoff; ++i) {
+ *p++ = ':';
+ p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t));
+ }
+ }
+
+ if (max_zoff != 8) {
+ *p++ = ':';
+
+ if (max_zoff + max_zlen == 8) {
+ *p++ = ':';
+ } else {
+ for (i = max_zoff + max_zlen; i < 8; ++i) {
+ *p++ = ':';
+ p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t));
+ }
+ }
+ }
+
+ *p = '\0';
+
+ 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;
+
+ for (i = 0; i < n; ++i) {
+ rv |= a[i] ^ b[i];
+ }
+
+ return rv == 0;
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h
new file mode 100644
index 0000000000..bd0145747c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h
@@ -0,0 +1,106 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_STR_H
+#define NGTCP2_STR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+void *ngtcp2_cpymem(void *dest, const void *src, size_t n);
+
+/*
+ * ngtcp2_setmem writes a string of length |n| consisting only |b| to
+ * the buffer pointed by |dest|. It returns dest + n;
+ */
+uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n);
+/*
+ * ngtcp2_encode_hex encodes |data| of length |len| in hex string. It
+ * writes additional NULL bytes at the end of the buffer. The buffer
+ * pointed by |dest| must have at least |len| * 2 + 1 bytes space.
+ * This function returns |dest|.
+ */
+uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len);
+
+/*
+ * ngtcp2_encode_ipv4 encodes binary form IPv4 address stored in
+ * |addr| to human readable text form in the buffer pointed by |dest|.
+ * The capacity of buffer must have enough length to store a text form
+ * plus a terminating NULL byte. The resulting text form ends with
+ * NULL byte. The function returns |dest|.
+ */
+uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr);
+
+/*
+ * ngtcp2_encode_ipv6 encodes binary form IPv6 address stored in
+ * |addr| to human readable text form in the buffer pointed by |dest|.
+ * The capacity of buffer must have enough length to store a text form
+ * plus a terminating NULL byte. The resulting text form ends with
+ * NULL byte. The function produces the canonical form of IPv6 text
+ * representation described in
+ * https://tools.ietf.org/html/rfc5952#section-4. The function
+ * returns |dest|.
+ */
+uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr);
+
+/*
+ * ngtcp2_encode_printable_ascii encodes |data| of length |len| in
+ * |dest| in the following manner: printable ascii characters are
+ * copied as is. The other characters are converted to ".". It
+ * writes additional NULL bytes at the end of the buffer. |dest| must
+ * have at least |len| + 1 bytes. This function returns |dest|.
+ */
+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.
+ */
+int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n);
+
+#endif /* NGTCP2_STR_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c
new file mode 100644
index 0000000000..8e8eef0c9c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c
@@ -0,0 +1,675 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_strm.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_rtb.h"
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_vec.h"
+
+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) {
+ strm->cycle = 0;
+ strm->tx.acked_offset = NULL;
+ strm->tx.cont_acked_offset = 0;
+ strm->tx.streamfrq = NULL;
+ strm->tx.offset = 0;
+ strm->tx.max_offset = max_tx_offset;
+ strm->tx.last_max_stream_data_ts = UINT64_MAX;
+ strm->rx.rob = NULL;
+ strm->rx.cont_offset = 0;
+ strm->rx.last_offset = 0;
+ strm->stream_id = stream_id;
+ strm->flags = 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) {
+ ngtcp2_ksl_it it;
+
+ if (strm == NULL) {
+ return;
+ }
+
+ 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_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);
+}
+
+static int strm_rob_init(ngtcp2_strm *strm) {
+ int rv;
+ ngtcp2_rob *rob = ngtcp2_mem_malloc(strm->mem, sizeof(*rob));
+
+ if (rob == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rv = ngtcp2_rob_init(rob, 8 * 1024, strm->mem);
+ if (rv != 0) {
+ ngtcp2_mem_free(strm->mem, rob);
+ return rv;
+ }
+
+ strm->rx.rob = rob;
+
+ return 0;
+}
+
+uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm) {
+ if (strm->rx.rob == NULL) {
+ return strm->rx.cont_offset;
+ }
+ return ngtcp2_rob_first_gap_offset(strm->rx.rob);
+}
+
+/* strm_rob_heavily_fragmented returns nonzero if the number of gaps
+ in |rob| exceeds the limit. */
+static int strm_rob_heavily_fragmented(ngtcp2_rob *rob) {
+ return ngtcp2_ksl_len(&rob->gapksl) >= 1000;
+}
+
+int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
+ size_t datalen, uint64_t offset) {
+ int rv;
+
+ if (strm->rx.rob == NULL) {
+ rv = strm_rob_init(strm);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (strm->rx.cont_offset) {
+ rv = ngtcp2_rob_remove_prefix(strm->rx.rob, strm->rx.cont_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ if (strm_rob_heavily_fragmented(strm->rx.rob)) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
+ return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen);
+}
+
+int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) {
+ if (strm->rx.rob == NULL) {
+ strm->rx.cont_offset = offset;
+ return 0;
+ }
+
+ return ngtcp2_rob_remove_prefix(strm->rx.rob, offset);
+}
+
+void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) {
+ strm->flags |= flags & NGTCP2_STRM_FLAG_SHUT_RDWR;
+}
+
+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;
+ }
+
+ strm->tx.streamfrq = streamfrq;
+
+ return 0;
+}
+
+int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) {
+ int rv;
+
+ assert(frc->fr.type == NGTCP2_FRAME_STREAM);
+ assert(frc->next == NULL);
+
+ if (strm->tx.streamfrq == NULL) {
+ rv = strm_streamfrq_init(strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset,
+ frc);
+}
+
+static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
+ ngtcp2_frame_chain **pfrc) {
+ ngtcp2_frame_chain *frc, *nfrc;
+ ngtcp2_stream *fr, *nfr;
+ uint64_t offset, end_offset;
+ size_t idx, end_idx;
+ uint64_t base_offset, end_base_offset;
+ ngtcp2_range gap;
+ ngtcp2_vec *v;
+ int rv;
+ ngtcp2_ksl_it it;
+
+ *pfrc = NULL;
+
+ assert(strm->tx.streamfrq);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq));
+
+ for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);) {
+ frc = ngtcp2_ksl_it_get(&it);
+ fr = &frc->fr.stream;
+
+ ngtcp2_ksl_remove(strm->tx.streamfrq, &it, &fr->offset);
+
+ idx = 0;
+ offset = fr->offset;
+ base_offset = 0;
+
+ gap = ngtcp2_strm_get_unacked_range_after(strm, offset);
+ if (gap.begin < offset) {
+ gap.begin = offset;
+ }
+
+ for (; idx < fr->datacnt && offset < gap.begin; ++idx) {
+ v = &fr->data[idx];
+ if (offset + v->len > gap.begin) {
+ base_offset = gap.begin - offset;
+ break;
+ }
+
+ offset += v->len;
+ }
+
+ if (idx == fr->datacnt) {
+ if (fr->fin) {
+ if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) {
+ ngtcp2_frame_chain_del(frc, strm->mem);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0);
+ return 0;
+ }
+
+ fr->offset = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt);
+ fr->datacnt = 0;
+
+ *pfrc = frc;
+
+ return 0;
+ }
+ ngtcp2_frame_chain_del(frc, strm->mem);
+ continue;
+ }
+
+ assert(gap.begin == offset + base_offset);
+
+ end_idx = idx;
+ end_offset = offset;
+ end_base_offset = 0;
+
+ for (; end_idx < fr->datacnt; ++end_idx) {
+ v = &fr->data[end_idx];
+ if (end_offset + v->len > gap.end) {
+ end_base_offset = gap.end - end_offset;
+ break;
+ }
+
+ end_offset += v->len;
+ }
+
+ if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) {
+ *pfrc = frc;
+ return 0;
+ }
+
+ if (fr->datacnt == end_idx) {
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx,
+ strm->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(frc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ memcpy(nfr->data, fr->data + end_idx,
+ sizeof(nfr->data[0]) * (fr->datacnt - end_idx));
+
+ assert(nfr->data[0].len > end_base_offset);
+
+ nfr->type = NGTCP2_FRAME_STREAM;
+ nfr->flags = 0;
+ nfr->fin = fr->fin;
+ nfr->stream_id = fr->stream_id;
+ nfr->offset = end_offset + end_base_offset;
+ nfr->datacnt = fr->datacnt - end_idx;
+ nfr->data[0].base += end_base_offset;
+ nfr->data[0].len -= (size_t)end_base_offset;
+
+ 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);
+ return rv;
+ }
+
+ if (end_base_offset) {
+ ++end_idx;
+ }
+
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->fin = 0;
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ if (end_base_offset) {
+ assert(fr->data[fr->datacnt - 1].len > end_base_offset);
+ fr->data[fr->datacnt - 1].len = (size_t)end_base_offset;
+ }
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ return 0;
+}
+
+int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
+ size_t left) {
+ ngtcp2_stream *fr, *nfr;
+ ngtcp2_frame_chain *frc, *nfrc;
+ int rv;
+ size_t nmerged;
+ size_t datalen;
+ ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT];
+ ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT];
+ size_t acnt, bcnt;
+ uint64_t unacked_offset;
+
+ if (strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0) {
+ *pfrc = NULL;
+ return 0;
+ }
+
+ rv = strm_streamfrq_unacked_pop(strm, &frc);
+ if (rv != 0) {
+ return rv;
+ }
+ if (frc == NULL) {
+ *pfrc = NULL;
+ return 0;
+ }
+
+ fr = &frc->fr.stream;
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (left == 0) {
+ /* datalen could be zero if 0 length STREAM has been sent */
+ if (datalen || ngtcp2_ksl_len(strm->tx.streamfrq) > 1) {
+ 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);
+ return rv;
+ }
+ *pfrc = NULL;
+ return 0;
+ }
+ }
+
+ if (datalen > left) {
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ bcnt = 0;
+ ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_STREAM_DATACNT);
+
+ assert(acnt > 0);
+ assert(bcnt > 0);
+
+ rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ nfr->type = NGTCP2_FRAME_STREAM;
+ nfr->flags = 0;
+ nfr->fin = fr->fin;
+ nfr->stream_id = fr->stream_id;
+ nfr->offset = fr->offset + left;
+ nfr->datacnt = bcnt;
+ ngtcp2_vec_copy(nfr->data, b, bcnt);
+
+ 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);
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ *nfr = *fr;
+ nfr->fin = 0;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_del(frc, strm->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+ }
+
+ left -= datalen;
+
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ for (; left && ngtcp2_ksl_len(strm->tx.streamfrq);) {
+ unacked_offset = ngtcp2_strm_streamfrq_unacked_offset(strm);
+ if (unacked_offset != fr->offset + datalen) {
+ assert(fr->offset + datalen < unacked_offset);
+ break;
+ }
+
+ rv = strm_streamfrq_unacked_pop(strm, &nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_del(frc, strm->mem);
+ return rv;
+ }
+ if (nfrc == NULL) {
+ break;
+ }
+
+ nfr = &nfrc->fr.stream;
+
+ if (nfr->fin && nfr->datacnt == 0) {
+ fr->fin = 1;
+ ngtcp2_frame_chain_del(nfrc, strm->mem);
+ break;
+ }
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left,
+ NGTCP2_MAX_STREAM_DATACNT);
+ if (nmerged == 0) {
+ 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);
+ return rv;
+ }
+ break;
+ }
+
+ datalen += nmerged;
+ left -= nmerged;
+
+ if (nfr->datacnt == 0) {
+ fr->fin = nfr->fin;
+ ngtcp2_frame_chain_del(nfrc, strm->mem);
+ continue;
+ }
+
+ nfr->offset += nmerged;
+
+ 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);
+ return rv;
+ }
+
+ break;
+ }
+
+ if (acnt == fr->datacnt) {
+ if (acnt > 0) {
+ fr->data[acnt - 1] = a[acnt - 1];
+ }
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ assert(acnt > fr->datacnt);
+
+ rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_del(frc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ *nfr = *fr;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_del(frc, strm->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+}
+
+uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_stream *fr;
+ ngtcp2_range gap;
+ ngtcp2_ksl_it it;
+ size_t datalen;
+
+ assert(strm->tx.streamfrq);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq));
+
+ for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ frc = ngtcp2_ksl_it_get(&it);
+ fr = &frc->fr.stream;
+
+ gap = ngtcp2_strm_get_unacked_range_after(strm, fr->offset);
+
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (gap.begin <= fr->offset) {
+ return fr->offset;
+ }
+ if (gap.begin < fr->offset + datalen) {
+ return gap.begin;
+ }
+ if (fr->offset + datalen == gap.begin && fr->fin &&
+ !(strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED)) {
+ return fr->offset + datalen;
+ }
+ }
+
+ return (uint64_t)-1;
+}
+
+ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm) {
+ ngtcp2_ksl_it it;
+
+ assert(strm->tx.streamfrq);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq));
+
+ it = ngtcp2_ksl_begin(strm->tx.streamfrq);
+ return ngtcp2_ksl_it_get(&it);
+}
+
+int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm) {
+ return strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0;
+}
+
+void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_ksl_it it;
+
+ if (strm->tx.streamfrq == NULL) {
+ return;
+ }
+
+ 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_ksl_clear(strm->tx.streamfrq);
+}
+
+int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm) {
+ return strm->pe.index != NGTCP2_PQ_BAD_INDEX;
+}
+
+int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) {
+ if (strm->tx.acked_offset == NULL) {
+ return strm->tx.cont_acked_offset == strm->tx.offset;
+ }
+
+ return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset) ==
+ strm->tx.offset;
+}
+
+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) {
+ gap.begin = strm->tx.cont_acked_offset;
+ gap.end = UINT64_MAX;
+ return gap;
+ }
+
+ gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset);
+ return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit);
+}
+
+uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) {
+ if (strm->tx.acked_offset == NULL) {
+ return strm->tx.cont_acked_offset;
+ }
+
+ return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset);
+}
+
+static int strm_acked_offset_init(ngtcp2_strm *strm) {
+ int rv;
+ ngtcp2_gaptr *acked_offset =
+ ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset));
+
+ if (acked_offset == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rv = ngtcp2_gaptr_init(acked_offset, strm->mem);
+ if (rv != 0) {
+ ngtcp2_mem_free(strm->mem, acked_offset);
+ return rv;
+ }
+
+ strm->tx.acked_offset = acked_offset;
+
+ return 0;
+}
+
+int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) {
+ int rv;
+
+ if (strm->tx.acked_offset == NULL) {
+ if (strm->tx.cont_acked_offset == offset) {
+ strm->tx.cont_acked_offset += len;
+ return 0;
+ }
+
+ rv = strm_acked_offset_init(strm);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv =
+ ngtcp2_gaptr_push(strm->tx.acked_offset, 0, strm->tx.cont_acked_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h
new file mode 100644
index 0000000000..6b7418706c
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h
@@ -0,0 +1,268 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_STRM_H
+#define NGTCP2_STRM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_rob.h"
+#include "ngtcp2_map.h"
+#include "ngtcp2_gaptr.h"
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_pq.h"
+
+typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
+
+/* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_STRM_FLAG_NONE 0x00
+/* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream
+ data is not allowed. */
+#define NGTCP2_STRM_FLAG_SHUT_RD 0x01
+/* 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_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
+/* 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
+/* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent
+ from the local endpoint. */
+#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10
+/* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM
+ is acknowledged by peer. */
+#define NGTCP2_STRM_FLAG_RST_ACKED 0x20
+/* 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
+
+typedef struct ngtcp2_strm ngtcp2_strm;
+
+struct ngtcp2_strm {
+ ngtcp2_map_entry me;
+ ngtcp2_pq_entry pe;
+ uint64_t cycle;
+
+ 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 {
+ /* 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;
+};
+
+/*
+ * 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);
+
+/*
+ * ngtcp2_strm_free deallocates memory allocated for |strm|. This
+ * function does not free the memory pointed by |strm| itself.
+ */
+void ngtcp2_strm_free(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_rx_offset returns the minimum offset of stream data
+ * which is not received yet.
+ */
+uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_recv_reordering handles reordered data.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
+ size_t datalen, uint64_t offset);
+
+/*
+ * ngtcp2_strm_update_rx_offset tells that data up to offset bytes are
+ * received in order.
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset);
+
+/*
+ * ngtcp2_strm_shutdown shutdowns |strm|. |flags| should be
+ * NGTCP2_STRM_FLAG_SHUT_RD, and/or NGTCP2_STRM_FLAG_SHUT_WR.
+ */
+void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags);
+
+/*
+ * ngtcp2_strm_streamfrq_push pushes |frc| to streamfrq for
+ * retransmission.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc);
+
+/*
+ * ngtcp2_strm_streamfrq_pop pops the first ngtcp2_frame_chain and
+ * assigns it to |*pfrc|. This function splits into or merges several
+ * ngtcp2_frame_chain objects so that the returned ngtcp2_frame_chain
+ * has at most |left| data length. If there is no frames to send,
+ * this function returns 0 and |*pfrc| is NULL.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
+ size_t left);
+
+/*
+ * ngtcp2_strm_streamfrq_unacked_offset returns the smallest offset of
+ * unacknowledged stream data held in strm->tx.streamfrq.
+ */
+uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_streamfrq_top returns the first ngtcp2_frame_chain.
+ * The queue must not be empty.
+ */
+ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_streamfrq_empty returns nonzero if streamfrq is empty.
+ */
+int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_streamfrq_clear removes all frames from streamfrq.
+ */
+void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_is_tx_queued returns nonzero if |strm| is queued.
+ */
+int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_is_all_tx_data_acked returns nonzero if all outgoing
+ * data for |strm| which have sent so far have been acknowledged.
+ */
+int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_get_unacked_range_after returns the range that is not
+ * acknowledged yet and intersects or comes after |offset|.
+ */
+ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm,
+ uint64_t offset);
+
+/*
+ * ngtcp2_strm_get_acked_offset returns offset, that is the data up to
+ * this offset have been acknowledged by a remote endpoint. It
+ * returns 0 if no data is acknowledged.
+ */
+uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_ack_data tells |strm| that the data [offset,
+ * offset+len) is acknowledged by a remote endpoint.
+ */
+int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len);
+
+#endif /* NGTCP2_STRM_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c
new file mode 100644
index 0000000000..7a6f8afa05
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c
@@ -0,0 +1,232 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_vec.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+
+ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) {
+ vec->base = (uint8_t *)base;
+ vec->len = len;
+ return vec;
+}
+
+int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen,
+ const ngtcp2_mem *mem) {
+ size_t len;
+ uint8_t *p;
+
+ len = sizeof(ngtcp2_vec) + datalen;
+
+ *pvec = ngtcp2_mem_malloc(mem, len);
+ if (*pvec == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ p = (uint8_t *)(*pvec) + sizeof(ngtcp2_vec);
+ (*pvec)->base = p;
+ (*pvec)->len = datalen;
+ if (datalen) {
+ /* p = */ ngtcp2_cpymem(p, data, datalen);
+ }
+
+ return 0;
+}
+
+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) {
+ size_t i;
+ size_t res = 0;
+
+ for (i = 0; i < n; ++i) {
+ res += vec[i].len;
+ }
+
+ return 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;
+ size_t srccnt = *psrccnt;
+ size_t nmove;
+ size_t extra = 0;
+
+ for (i = 0; i < srccnt; ++i) {
+ if (left >= src[i].len) {
+ left -= src[i].len;
+ continue;
+ }
+
+ if (*pdstcnt && src[srccnt - 1].base + src[srccnt - 1].len == dst[0].base) {
+ if (*pdstcnt + srccnt - i - 1 > maxcnt) {
+ return -1;
+ }
+
+ dst[0].len += src[srccnt - 1].len;
+ dst[0].base = src[srccnt - 1].base;
+ extra = src[srccnt - 1].len;
+ --srccnt;
+ } else if (*pdstcnt + srccnt - i > maxcnt) {
+ return -1;
+ }
+
+ if (left == 0) {
+ *psrccnt = i;
+ } else {
+ *psrccnt = i + 1;
+ }
+
+ nmove = srccnt - i;
+ if (nmove) {
+ memmove(dst + nmove, dst, sizeof(ngtcp2_vec) * (*pdstcnt));
+ *pdstcnt += nmove;
+ memcpy(dst, src + i, sizeof(ngtcp2_vec) * nmove);
+ }
+
+ dst[0].len -= left;
+ dst[0].base += left;
+ src[i].len = left;
+
+ if (nmove == 0) {
+ extra -= left;
+ }
+
+ return (ngtcp2_ssize)(ngtcp2_vec_len(dst, nmove) + extra);
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
+ size_t *psrccnt, size_t left, size_t maxcnt) {
+ size_t orig_left = left;
+ size_t i;
+ ngtcp2_vec *a, *b;
+
+ assert(maxcnt);
+
+ if (*pdstcnt == 0) {
+ if (*psrccnt == 0) {
+ return 0;
+ }
+
+ a = &dst[0];
+ b = &src[0];
+
+ if (left >= b->len) {
+ *a = *b;
+ ++*pdstcnt;
+ left -= b->len;
+ i = 1;
+ } else {
+ a->len = left;
+ a->base = b->base;
+
+ b->len -= left;
+ b->base += left;
+
+ return left;
+ }
+ } else {
+ i = 0;
+ }
+
+ for (; left && i < *psrccnt; ++i) {
+ a = &dst[*pdstcnt - 1];
+ b = &src[i];
+
+ if (left >= b->len) {
+ if (a->base + a->len == b->base) {
+ a->len += b->len;
+ } else if (*pdstcnt == maxcnt) {
+ break;
+ } else {
+ dst[(*pdstcnt)++] = *b;
+ }
+ left -= b->len;
+ continue;
+ }
+
+ if (a->base + a->len == b->base) {
+ a->len += left;
+ } else if (*pdstcnt == maxcnt) {
+ break;
+ } else {
+ dst[*pdstcnt].len = left;
+ dst[*pdstcnt].base = b->base;
+ ++*pdstcnt;
+ }
+
+ b->len -= left;
+ b->base += left;
+ left = 0;
+
+ break;
+ }
+
+ memmove(src, src + i, sizeof(ngtcp2_vec) * (*psrccnt - i));
+ *psrccnt -= i;
+
+ 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 i, j;
+ size_t len = left;
+
+ *pnwritten = 0;
+
+ for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) {
+ if (src[i].len == 0) {
+ ++i;
+ continue;
+ }
+ dst[j] = src[i];
+ if (dst[j].len > left) {
+ dst[j].len = left;
+ *pnwritten = len;
+ return j + 1;
+ }
+ left -= dst[j].len;
+ ++i;
+ ++j;
+ }
+
+ *pnwritten = len - left;
+
+ return j;
+}
+
+void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt) {
+ memcpy(dst, src, sizeof(ngtcp2_vec) * cnt);
+}
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h
new file mode 100644
index 0000000000..077820a9ef
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h
@@ -0,0 +1,114 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_VEC_H
+#define NGTCP2_VEC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+/*
+ * ngtcp2_vec_lit is a convenient macro to fill the object pointed by
+ * |DEST| with the literal string |LIT|.
+ */
+#define ngtcp2_vec_lit(DEST, LIT) \
+ ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST))
+
+/*
+ * ngtcp2_vec_init initializes |vec| with the given parameters. It
+ * returns |vec|.
+ */
+ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len);
+
+/*
+ * ngtcp2_vec_new allocates and initializes |*pvec| with given |data|
+ * of length |datalen|. This function allocates memory for |*pvec|
+ * and the given data with a single allocation, and the contents
+ * pointed by |data| is copied into the allocated memory space. To
+ * free the allocated memory, call ngtcp2_vec_del.
+ */
+int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_vec_del frees the memory allocated by |vec| which is
+ * allocated and initialized by ngtcp2_vec_new.
+ */
+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);
+
+/*
+ * ngtcp2_vec_split splits |src| to |dst| so that the sum of the
+ * length in |src| does not exceed |left| bytes. The |maxcnt| is the
+ * maximum number of elements which |dst| array can contain. The
+ * caller must set |*psrccnt| to the number of elements of |src|.
+ * Similarly, the caller must set |*pdstcnt| to the number of elements
+ * of |dst|. The split does not necessarily occur at the boundary of
+ * ngtcp2_vec object. After split has done, this function updates
+ * |*psrccnt| and |*pdstcnt|. This function returns the number of
+ * bytes moved from |src| to |dst|. If split cannot be made because
+ * doing so exceeds |maxcnt|, this function returns -1.
+ */
+ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst,
+ size_t *pdstcnt, size_t left, size_t maxcnt);
+
+/*
+ * ngtcp2_vec_merge merges |src| into |dst| by moving at most |left|
+ * bytes from |src|. The |maxcnt| is the maximum number of elements
+ * which |dst| array can contain. The caller must set |*pdstcnt| to
+ * the number of elements of |dst|. Similarly, the caller must set
+ * |*psrccnt| to the number of elements of |src|. After merge has
+ * done, this function updates |*psrccnt| and |*pdstcnt|. This
+ * function returns the number of bytes moved from |src| to |dst|.
+ */
+size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
+ size_t *psrccnt, size_t left, size_t maxcnt);
+
+/*
+ * 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.
+ */
+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);
+
+/*
+ * ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must
+ * have sufficient capacity.
+ */
+void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt);
+
+#endif /* NGTCP2_VEC_H */
diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c
new file mode 100644
index 0000000000..40f3ae3f9e
--- /dev/null
+++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c
@@ -0,0 +1,39 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM,
+ NGTCP2_VERSION};
+
+ngtcp2_info *ngtcp2_version(int least_version) {
+ if (least_version > NGTCP2_VERSION_NUM) {
+ return NULL;
+ }
+ return &version;
+}
diff --git a/node.gypi b/node.gypi
index f9dba2d4bd..dde4f8332d 100644
--- a/node.gypi
+++ b/node.gypi
@@ -326,6 +326,9 @@
# For tests
'./deps/openssl/openssl.gyp:openssl-cli',
],
+ # Set 1.0.0 as the API compability level to avoid the
+ # deprecation warnings when using OpenSSL 3.0.
+ 'defines': ['OPENSSL_API_COMPAT=0x10000000L'],
'conditions': [
# -force_load or --whole-archive are not applicable for
# the static library
@@ -360,16 +363,17 @@
}],
],
}],
- ],
- }, {
- # Set 1.0.0 as the API compability level to avoid the
- # deprecation warnings when using OpenSSL 3.0.
- 'defines': ['OPENSSL_API_COMPAT=0x10000000L'],
- }]]
-
+ ]
+ }],
+ [ 'openssl_quic=="true" and node_shared_ngtcp2=="false"', {
+ 'dependencies': [ './deps/ngtcp2/ngtcp2.gyp:ngtcp2' ]
+ }],
+ [ 'openssl_quic=="true" and node_shared_nghttp3=="false"', {
+ 'dependencies': [ './deps/ngtcp2/ngtcp2.gyp:nghttp3' ]
+ }]
+ ]
}, {
'defines': [ 'HAVE_OPENSSL=0' ]
}],
-
],
}
diff --git a/src/node_metadata.cc b/src/node_metadata.cc
index 8d0a725de4..46d9be0dfc 100644
--- a/src/node_metadata.cc
+++ b/src/node_metadata.cc
@@ -13,6 +13,11 @@
#include <openssl/opensslv.h>
#endif // HAVE_OPENSSL
+#ifdef OPENSSL_INFO_QUIC
+#include <ngtcp2/version.h>
+#include <nghttp3/version.h>
+#endif
+
#ifdef NODE_HAVE_I18N_SUPPORT
#include <unicode/timezone.h>
#include <unicode/ulocdata.h>
@@ -95,6 +100,11 @@ Metadata::Versions::Versions() {
icu = U_ICU_VERSION;
unicode = U_UNICODE_VERSION;
#endif // NODE_HAVE_I18N_SUPPORT
+
+#ifdef OPENSSL_INFO_QUIC
+ ngtcp2 = NGTCP2_VERSION;
+ nghttp3 = NGHTTP3_VERSION;
+#endif
}
Metadata::Release::Release() : name(NODE_RELEASE) {
diff --git a/src/node_metadata.h b/src/node_metadata.h
index bf7e5d3ff4..4486d5af2c 100644
--- a/src/node_metadata.h
+++ b/src/node_metadata.h
@@ -6,6 +6,10 @@
#include <string>
#include "node_version.h"
+#if HAVE_OPENSSL
+#include <openssl/crypto.h>
+#endif // HAVE_OPENSSL
+
namespace node {
// if this is a release build and no explicit base has been set
@@ -48,10 +52,19 @@ namespace node {
#define NODE_VERSIONS_KEY_INTL(V)
#endif // NODE_HAVE_I18N_SUPPORT
+#ifdef OPENSSL_INFO_QUIC
+#define NODE_VERSIONS_KEY_QUIC(V) \
+ V(ngtcp2) \
+ V(nghttp3)
+#else
+#define NODE_VERSIONS_KEY_QUIC(V)
+#endif
+
#define NODE_VERSIONS_KEYS(V) \
NODE_VERSIONS_KEYS_BASE(V) \
NODE_VERSIONS_KEY_CRYPTO(V) \
- NODE_VERSIONS_KEY_INTL(V)
+ NODE_VERSIONS_KEY_INTL(V) \
+ NODE_VERSIONS_KEY_QUIC(V)
class Metadata {
public:
diff --git a/test/common/index.js b/test/common/index.js
index 89c4fcaaa9..894c8025e7 100644
--- a/test/common/index.js
+++ b/test/common/index.js
@@ -54,6 +54,8 @@ const hasCrypto = Boolean(process.versions.openssl) &&
const hasOpenSSL3 = hasCrypto &&
require('crypto').constants.OPENSSL_VERSION_NUMBER >= 805306368;
+const hasQuic = hasCrypto && !!process.config.variables.openssl_quic;
+
// Check for flags. Skip this for workers (both, the `cluster` module and
// `worker_threads`) and child processes.
// If the binary was built without-ssl then the crypto flags are
@@ -730,6 +732,7 @@ const common = {
hasIntl,
hasCrypto,
hasOpenSSL3,
+ hasQuic,
hasMultiLocalhost,
invalidArgTypeHelper,
isAIX,
diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js
index 666c8ead3b..464a8a4ece 100644
--- a/test/parallel/test-process-versions.js
+++ b/test/parallel/test-process-versions.js
@@ -2,13 +2,28 @@
const common = require('../common');
const assert = require('assert');
-const expected_keys = ['ares', 'brotli', 'modules', 'node',
- 'uv', 'v8', 'zlib', 'nghttp2', 'napi', 'llhttp'];
+const expected_keys = [
+ 'ares',
+ 'brotli',
+ 'modules',
+ 'node',
+ 'uv',
+ 'v8',
+ 'zlib',
+ 'nghttp2',
+ 'napi',
+ 'llhttp'
+];
if (common.hasCrypto) {
expected_keys.push('openssl');
}
+if (common.hasQuic) {
+ expected_keys.push('ngtcp2');
+ expected_keys.push('nghttp3');
+}
+
if (common.hasIntl) {
expected_keys.push('icu');
expected_keys.push('cldr');
diff --git a/tools/getsharedopensslhasquic.py b/tools/getsharedopensslhasquic.py
index 549a0eacda..00d4b4b424 100644
--- a/tools/getsharedopensslhasquic.py
+++ b/tools/getsharedopensslhasquic.py
@@ -3,17 +3,18 @@ import os
import re
def get_has_quic(include_path):
- openssl_crypto_h = os.path.join(
- include_path,
- 'openssl',
- 'crypto.h')
+ if include_path:
+ openssl_crypto_h = os.path.join(
+ include_path,
+ 'openssl',
+ 'crypto.h')
- f = open(openssl_crypto_h)
+ f = open(openssl_crypto_h)
- regex = '^#\s*define OPENSSL_INFO_QUIC'
+ regex = '^#\s*define OPENSSL_INFO_QUIC'
- for line in f:
- if (re.match(regex, line)):
- return True
+ for line in f:
+ if (re.match(regex, line)):
+ return True
return False
diff --git a/tools/license-builder.sh b/tools/license-builder.sh
index d1d173943c..05a15471c7 100755
--- a/tools/license-builder.sh
+++ b/tools/license-builder.sh
@@ -113,5 +113,7 @@ addlicense "rimraf" "lib/internal/fs/rimraf.js" \
"$(curl -sL https://raw.githubusercontent.com/isaacs/rimraf/0e365ac4e4d64a25aa2a3cc026348f13410210e1/LICENSE)"
addlicense "uvwasi" "deps/uvwasi" "$(cat "${rootdir}"/deps/uvwasi/LICENSE)"
+addlicense "ngtcp2" "deps/ngtcp2/ngtcp2/" "$(cat "${rootdir}"/deps/ngtcp2/LICENSE_ngtcp2)"
+addlicense "nghttp3" "deps/ngtcp2/nghttp3/" "$(cat "${rootdir}"/deps/ngtcp2/LICENSE_nghttp3)"
mv "$tmplicense" "$licensefile"