diff -pruN 1.52.0-1/.github/workflows/build.yml 1.53.0-1/.github/workflows/build.yml
--- 1.52.0-1/.github/workflows/build.yml	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/.github/workflows/build.yml	2023-05-10 08:27:53.000000000 +0000
@@ -2,8 +2,177 @@ name: build
 
 on: [push, pull_request]
 
+permissions: read-all
+
+env:
+  LIBBPF_VERSION: v1.1.0
+  OPENSSL1_VERSION: 1_1_1t+quic
+  OPENSSL3_VERSION: 3.1.0+quic
+  BORINGSSL_VERSION: b0b1f9dfc583c96d5f91b7f8cdb7efabcf22793b
+  NGHTTP3_VERSION: v0.11.0
+  NGTCP2_VERSION: v0.15.0
+
 jobs:
+  build-cache:
+    strategy:
+      matrix:
+        os: [ubuntu-22.04, macos-11]
+
+    runs-on: ${{ matrix.os }}
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Restore libbpf cache
+      id: cache-libbpf
+      uses: actions/cache@v3
+      if: runner.os == 'Linux'
+      with:
+        path: libbpf/build
+        key: ${{ runner.os }}-libbpf-${{ env.LIBBPF_VERSION }}
+    - name: Restore OpenSSL v1.1.1 cache
+      id: cache-openssl1
+      uses: actions/cache@v3
+      with:
+        path: openssl1/build
+        key: ${{ runner.os }}-openssl-${{ env.OPENSSL1_VERSION }}
+    - name: Restore OpenSSL v3.x cache
+      id: cache-openssl3
+      uses: actions/cache@v3
+      with:
+        path: openssl3/build
+        key: ${{ runner.os }}-openssl-${{ env.OPENSSL3_VERSION }}
+    - name: Restore BoringSSL cache
+      id: cache-boringssl
+      uses: actions/cache@v3
+      with:
+        path: |
+          boringssl/build/crypto/libcrypto.a
+          boringssl/build/ssl/libssl.a
+          boringssl/include
+        key: ${{ runner.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
+    - name: Restore nghttp3 cache
+      id: cache-nghttp3
+      uses: actions/cache@v3
+      with:
+        path: nghttp3/build
+        key: ${{ runner.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
+    - name: Restore ngtcp2 + quictls/openssl v1.1.1 cache
+      id: cache-ngtcp2-openssl1
+      uses: actions/cache@v3
+      with:
+        path: ngtcp2-openssl1/build
+        key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
+    - name: Restore ngtcp2 + quictls/openssl v3.x cache
+      id: cache-ngtcp2-openssl3
+      uses: actions/cache@v3
+      with:
+        path: ngtcp2-openssl3/build
+        key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL3_VERSION }}
+    - id: settings
+      if: |
+        (steps.cache-libbpf.outputs.cache-hit != 'true' && runner.os == 'Linux') ||
+        steps.cache-openssl1.outputs.cache-hit != 'true' ||
+        steps.cache-openssl3.outputs.cache-hit != 'true' ||
+        steps.cache-boringssl.outputs.cache-hit != 'true' ||
+        steps.cache-nghttp3.outputs.cache-hit != 'true' ||
+        steps.cache-ngtcp2-openssl1.outputs.cache-hit != 'true' ||
+        steps.cache-ngtcp2-openssl3.outputs.cache-hit != 'true'
+      run: |
+        echo 'needs-build=true' >> $GITHUB_OUTPUT
+    - name: Linux setup
+      if: runner.os == 'Linux' && steps.settings.outputs.needs-build == 'true'
+      run: |
+        sudo apt-get install \
+          g++-12 \
+          clang-14 \
+          autoconf \
+          automake \
+          autotools-dev \
+          libtool \
+          pkg-config \
+          libelf-dev \
+          cmake \
+          cmake-data
+    - name: MacOS setup
+      if: runner.os == 'macOS' && steps.settings.outputs.needs-build == 'true'
+      run: |
+        brew install \
+          autoconf \
+          automake \
+          pkg-config \
+          libtool
+    - name: Build libbpf
+      if: steps.cache-libbpf.outputs.cache-hit != 'true' && runner.os == 'Linux'
+      run: |
+        git clone -b ${{ env.LIBBPF_VERSION }} https://github.com/libbpf/libbpf
+        cd libbpf
+        make -C src install PREFIX=$PWD/build
+    - name: Build quictls/openssl v1.1.1
+      if: steps.cache-openssl1.outputs.cache-hit != 'true'
+      run: |
+        git clone --depth 1 -b OpenSSL_${{ env.OPENSSL1_VERSION }} https://github.com/quictls/openssl openssl1
+        cd openssl1
+        ./config --prefix=$PWD/build
+        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
+        make install_sw
+    - name: Build quictls/openssl v3.x
+      if: steps.cache-openssl3.outputs.cache-hit != 'true'
+      run: |
+        git clone --depth 1 -b openssl-${{ env.OPENSSL3_VERSION }} https://github.com/quictls/openssl openssl3
+        cd openssl3
+        ./config enable-ktls --prefix=$PWD/build --libdir=$PWD/build/lib
+        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
+        make install_sw
+    - name: Build BoringSSL
+      if: steps.cache-boringssl.outputs.cache-hit != 'true'
+      run: |
+        git clone https://boringssl.googlesource.com/boringssl
+        cd boringssl
+        git checkout ${{ env.BORINGSSL_VERSION }}
+        mkdir build
+        cd build
+        cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..
+        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
+    - name: Build nghttp3
+      if: steps.cache-nghttp3.outputs.cache-hit != 'true'
+      run: |
+        git clone --depth 1 -b ${{ env.NGHTTP3_VERSION}} https://github.com/ngtcp2/nghttp3
+        cd nghttp3
+        autoreconf -i
+        ./configure --prefix=$PWD/build --enable-lib-only
+        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
+        make install
+    - name: Build ngtcp2 + quictls/openssl v1.1.1
+      if: steps.cache-ngtcp2-openssl1.outputs.cache-hit != 'true'
+      run: |
+        git clone --depth 1 -b ${{ env.NGTCP2_VERSION }} https://github.com/ngtcp2/ngtcp2 ngtcp2-openssl1
+        cd ngtcp2-openssl1
+        autoreconf -i
+        ./configure --prefix=$PWD/build --enable-lib-only \
+          PKG_CONFIG_PATH="../openssl1/build/lib/pkgconfig" \
+          BORINGSSL_CFLAGS="-I$PWD/../boringssl/include/" \
+          BORINGSSL_LIBS="-L$PWD/../boringssl/build/ssl -lssl -L$PWD/../boringssl/build/crypto -lcrypto" \
+          --with-boringssl
+        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
+        make install
+    - name: Build ngtcp2 + quictls/openssl v3.x
+      if: steps.cache-ngtcp2-openssl3.outputs.cache-hit != 'true'
+      run: |
+        git clone --depth 1 -b ${{ env.NGTCP2_VERSION }} https://github.com/ngtcp2/ngtcp2 ngtcp2-openssl3
+        cd ngtcp2-openssl3
+        autoreconf -i
+        ./configure --prefix=$PWD/build --enable-lib-only \
+          PKG_CONFIG_PATH="../openssl3/build/lib/pkgconfig" \
+          BORINGSSL_CFLAGS="-I$PWD/../boringssl/include/" \
+          BORINGSSL_LIBS="-L$PWD/../boringssl/build/ssl -lssl -L$PWD/../boringssl/build/crypto -lcrypto" \
+          --with-boringssl
+        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
+        make install
+
   build:
+    needs:
+    - build-cache
+
     strategy:
       matrix:
         os: [ubuntu-22.04, macos-11]
@@ -91,83 +260,87 @@ jobs:
       run: |
         echo 'CC=gcc' >> $GITHUB_ENV
         echo 'CXX=g++' >> $GITHUB_ENV
-    - name: Build libbpf
+    - name: Restore libbpf cache
+      uses: actions/cache/restore@v3
+      if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
+      with:
+        path: libbpf/build
+        key: ${{ runner.os }}-libbpf-${{ env.LIBBPF_VERSION }}
+        fail-on-cache-miss: true
+    - name: Set libbpf variables
       if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
       run: |
-        git clone -b v1.1.0 https://github.com/libbpf/libbpf
         cd libbpf
-        PREFIX=$PWD/build make -C src install
 
         EXTRA_AUTOTOOLS_OPTS="--with-libbpf"
         EXTRA_CMAKE_OPTS="-DWITH_LIBBPF=1"
 
         echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
         echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
-    - name: Build quictls/openssl v1.1.1
+    - name: Restore quictls/openssl v1.1.1 cache
+      uses: actions/cache/restore@v3
       if: matrix.http3 == 'http3' && matrix.openssl == 'openssl1'
-      run: |
-        git clone --depth 1 -b OpenSSL_1_1_1t+quic https://github.com/quictls/openssl
-        cd openssl
-        ./config --prefix=$PWD/build
-        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
-        make install_sw
-    - name: Build quictls/openssl v3.0.x
+      with:
+        path: openssl1/build
+        key: ${{ runner.os }}-openssl-${{ env.OPENSSL1_VERSION }}
+        fail-on-cache-miss: true
+    - name: Restore quictls/openssl v3.x cache
+      uses: actions/cache/restore@v3
       if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3'
-      run: |
-        unset CPPFLAGS
-        unset LDFLAGS
-
-        git clone --depth 1 -b openssl-3.0.8+quic https://github.com/quictls/openssl
-        cd openssl
-        ./config enable-ktls --prefix=$PWD/build --libdir=$PWD/build/lib
-        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
-        make install_sw
-    - name: Build BoringSSL
+      with:
+        path: openssl3/build
+        key: ${{ runner.os }}-openssl-${{ env.OPENSSL3_VERSION }}
+        fail-on-cache-miss: true
+    - name: Restore BoringSSL cache
+      uses: actions/cache/restore@v3
+      if: matrix.openssl == 'boringssl'
+      with:
+        path: |
+          boringssl/build/crypto/libcrypto.a
+          boringssl/build/ssl/libssl.a
+          boringssl/include
+        key: ${{ runner.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
+        fail-on-cache-miss: true
+    - name: Set BoringSSL variables
       if: matrix.openssl == 'boringssl'
       run: |
-        git clone https://boringssl.googlesource.com/boringssl
         cd boringssl
-        git checkout 80a243e07ef77156af66efa7d22ac35aba44c1b3
-        mkdir build
-        cd build
-        cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..
-        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
-        cd ..
 
         OPENSSL_CFLAGS="-I$PWD/include/"
         OPENSSL_LIBS="-L$PWD/build/ssl -lssl -L$PWD/build/crypto -lcrypto -pthread"
-        EXTRA_NGTCP2_OPTS="$EXTRA_NGTCP2_OPTS --without-openssl --with-boringssl"
         EXTRA_AUTOTOOLS_OPTS="$EXTRA_AUTOTOOLS_OPTS --without-neverbleed --without-jemalloc"
 
         echo 'OPENSSL_CFLAGS='"$OPENSSL_CFLAGS" >> $GITHUB_ENV
         echo 'OPENSSL_LIBS='"$OPENSSL_LIBS" >> $GITHUB_ENV
         echo 'BORINGSSL_CFLAGS='"$OPENSSL_CFLAGS" >> $GITHUB_ENV
         echo 'BORINGSSL_LIBS='"$OPENSSL_LIBS" >> $GITHUB_ENV
-        echo 'EXTRA_NGTCP2_OPTS='"$EXTRA_NGTCP2_OPTS" >> "$GITHUB_ENV"
         echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
-    - name: Build nghttp3
-      if: matrix.http3 == 'http3'
-      run: |
-        git clone --depth 1 -b v0.8.0 https://github.com/ngtcp2/nghttp3
-        cd nghttp3
-        autoreconf -i
-        ./configure --prefix=$PWD/build --enable-lib-only
-        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
-        make install
-    - name: Build ngtcp2
+    - name: Restore nghttp3 cache
+      uses: actions/cache/restore@v3
       if: matrix.http3 == 'http3'
-      run: |
-        git clone --depth 1 -b v0.13.1 https://github.com/ngtcp2/ngtcp2
-        cd ngtcp2
-        autoreconf -i
-        ./configure --prefix=$PWD/build --enable-lib-only PKG_CONFIG_PATH="../openssl/build/lib/pkgconfig" $EXTRA_NGTCP2_OPTS
-        make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
-        make install
+      with:
+        path: nghttp3/build
+        key: ${{ runner.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
+        fail-on-cache-miss: true
+    - name: Restore ngtcp2 + quictls/openssl v1.1.1 cache
+      uses: actions/cache/restore@v3
+      if: matrix.http3 == 'http3' && (matrix.openssl == 'openssl1' || matrix.openssl == 'boringssl')
+      with:
+        path: ngtcp2-openssl1/build
+        key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
+        fail-on-cache-miss: true
+    - name: Restore ngtcp2 + quictls/openssl v3.x cache
+      uses: actions/cache/restore@v3
+      if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3'
+      with:
+        path: ngtcp2-openssl3/build
+        key: ${{ runner.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL3_VERSION }}
+        fail-on-cache-miss: true
     - name: Setup extra environment variables for HTTP/3
       if: matrix.http3 == 'http3'
       run: |
-        PKG_CONFIG_PATH="$PWD/openssl/build/lib/pkgconfig:$PWD/nghttp3/build/lib/pkgconfig:$PWD/ngtcp2/build/lib/pkgconfig:$PWD/libbpf/build/lib64/pkgconfig:$PKG_CONFIG_PATH"
-        LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/openssl/build/lib -Wl,-rpath,$PWD/libbpf/build/lib64"
+        PKG_CONFIG_PATH="$PWD/openssl1/build/lib/pkgconfig:$PWD/openssl3/build/lib/pkgconfig:$PWD/nghttp3/build/lib/pkgconfig:$PWD/ngtcp2-openssl1/build/lib/pkgconfig:$PWD/ngtcp2-openssl3/build/lib/pkgconfig:$PWD/libbpf/build/lib64/pkgconfig:$PKG_CONFIG_PATH"
+        LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/openssl1/build/lib -Wl,-rpath,$PWD/openssl3/build/lib -Wl,-rpath,$PWD/libbpf/build/lib64"
         EXTRA_AUTOTOOLS_OPTS="--enable-http3 $EXTRA_AUTOTOOLS_OPTS"
         EXTRA_CMAKE_OPTS="-DENABLE_HTTP3=1 $EXTRA_CMAKE_OPTS"
 
@@ -221,6 +394,10 @@ jobs:
         cd $NGHTTP2_CMAKE_DIR
         make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
         make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" check
+    - uses: actions/setup-go@v3
+      if: matrix.buildtool == 'cmake'
+      with:
+        go-version: '1.19'
     - name: Integration test
       # Integration tests for nghttpx; autotools erases build
       # artifacts.
@@ -277,15 +454,28 @@ jobs:
         wine main.exe
 
   build-windows:
+    strategy:
+      matrix:
+        arch: [x86, x64]
+        include:
+        - arch: x86
+          platform: Win32
+        - arch: x64
+          platform: x64
+
     runs-on: windows-latest
 
     steps:
     - uses: actions/checkout@v3
+    - uses: microsoft/setup-msbuild@v1
+    - run: |
+        vcpkg --triplet=${{ matrix.arch }}-windows install cunit
     - name: Configure cmake
       run: |
         mkdir build
         cd build
-        cmake ..
+        cmake -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_GENERATOR_PLATFORM=${{ matrix.platform }} -DVCPKG_TARGET_TRIPLET=${{ matrix.arch}}-windows ..
     - name: Build nghttp2
       run: |
         cmake --build build
+        cmake --build build --target check
diff -pruN 1.52.0-1/.github/workflows/fuzz.yml 1.53.0-1/.github/workflows/fuzz.yml
--- 1.52.0-1/.github/workflows/fuzz.yml	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/.github/workflows/fuzz.yml	2023-05-10 08:27:53.000000000 +0000
@@ -1,5 +1,6 @@
 name: CIFuzz
 on: [pull_request]
+permissions: read-all
 jobs:
   Fuzzing:
     runs-on: ubuntu-latest
diff -pruN 1.52.0-1/CMakeLists.txt 1.53.0-1/CMakeLists.txt
--- 1.52.0-1/CMakeLists.txt	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/CMakeLists.txt	2023-05-10 08:27:53.000000000 +0000
@@ -24,12 +24,12 @@
 
 cmake_minimum_required(VERSION 3.0)
 # XXX using 1.8.90 instead of 1.9.0-DEV
-project(nghttp2 VERSION 1.52.0)
+project(nghttp2 VERSION 1.53.0)
 
 # See versioning rule:
 #  https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
 set(LT_CURRENT  38)
-set(LT_REVISION 1)
+set(LT_REVISION 2)
 set(LT_AGE      24)
 
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
diff -pruN 1.52.0-1/README.rst 1.53.0-1/README.rst
--- 1.52.0-1/README.rst	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/README.rst	2023-05-10 08:27:53.000000000 +0000
@@ -129,9 +129,9 @@ following libraries are required:
 * `OpenSSL with QUIC support
   <https://github.com/quictls/openssl/tree/OpenSSL_1_1_1t+quic>`_; or
   `BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit
-  80a243e07ef77156af66efa7d22ac35aba44c1b3)
-* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ >= 0.13.0
-* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 0.7.0
+  b0b1f9dfc583c96d5f91b7f8cdb7efabcf22793b)
+* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ >= 0.15.0
+* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 0.9.0
 
 Use ``--enable-http3`` configure option to enable HTTP/3 feature for
 h2load and nghttpx.
@@ -354,7 +354,7 @@ Build nghttp3:
 
 .. code-block:: text
 
-   $ git clone --depth 1 -b v0.8.0 https://github.com/ngtcp2/nghttp3
+   $ git clone --depth 1 -b v0.11.0 https://github.com/ngtcp2/nghttp3
    $ cd nghttp3
    $ autoreconf -i
    $ ./configure --prefix=$PWD/build --enable-lib-only
@@ -366,7 +366,7 @@ Build ngtcp2:
 
 .. code-block:: text
 
-   $ git clone --depth 1 -b v0.13.1 https://github.com/ngtcp2/ngtcp2
+   $ git clone --depth 1 -b v0.15.0 https://github.com/ngtcp2/ngtcp2
    $ cd ngtcp2
    $ autoreconf -i
    $ ./configure --prefix=$PWD/build --enable-lib-only \
diff -pruN 1.52.0-1/configure.ac 1.53.0-1/configure.ac
--- 1.52.0-1/configure.ac	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/configure.ac	2023-05-10 08:27:53.000000000 +0000
@@ -25,7 +25,7 @@ dnl Do not change user variables!
 dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
 
 AC_PREREQ(2.61)
-AC_INIT([nghttp2], [1.52.0], [t-tujikawa@users.sourceforge.net])
+AC_INIT([nghttp2], [1.53.0], [t-tujikawa@users.sourceforge.net])
 AC_CONFIG_AUX_DIR([.])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
@@ -45,7 +45,7 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_R
 dnl See versioning rule:
 dnl  https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
 AC_SUBST(LT_CURRENT, 38)
-AC_SUBST(LT_REVISION, 1)
+AC_SUBST(LT_REVISION, 2)
 AC_SUBST(LT_AGE, 24)
 
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
@@ -504,7 +504,7 @@ fi
 # ngtcp2 (for src)
 have_libngtcp2=no
 if test "x${request_libngtcp2}" != "xno"; then
-  PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.13.0], [have_libngtcp2=yes],
+  PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 0.15.0], [have_libngtcp2=yes],
                     [have_libngtcp2=no])
   if test "x${have_libngtcp2}" = "xno"; then
     AC_MSG_NOTICE($LIBNGTCP2_PKG_ERRORS)
@@ -521,7 +521,7 @@ have_libngtcp2_crypto_openssl=no
 if test "x${have_ssl_is_quic}" = "xyes" &&
    test "x${request_libngtcp2}" != "xno"; then
   PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_OPENSSL],
-                    [libngtcp2_crypto_openssl >= 0.13.0],
+                    [libngtcp2_crypto_openssl >= 0.15.0],
                     [have_libngtcp2_crypto_openssl=yes],
                     [have_libngtcp2_crypto_openssl=no])
   if test "x${have_libngtcp2_crypto_openssl}" = "xno"; then
@@ -563,7 +563,7 @@ fi
 # nghttp3 (for src)
 have_libnghttp3=no
 if test "x${request_libnghttp3}" != "xno"; then
-  PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.7.0], [have_libnghttp3=yes],
+  PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.9.0], [have_libnghttp3=yes],
                     [have_libnghttp3=no])
   if test "x${have_libnghttp3}" = "xno"; then
     AC_MSG_NOTICE($LIBNGHTTP3_PKG_ERRORS)
@@ -1072,6 +1072,7 @@ AC_CONFIG_FILES([
   tests/testdata/Makefile
   third-party/Makefile
   src/Makefile
+  src/testdata/Makefile
   bpf/Makefile
   examples/Makefile
   integration-tests/Makefile
diff -pruN 1.52.0-1/debian/changelog 1.53.0-1/debian/changelog
--- 1.52.0-1/debian/changelog	2023-02-20 18:18:10.000000000 +0000
+++ 1.53.0-1/debian/changelog	2023-05-21 13:46:26.000000000 +0000
@@ -1,3 +1,9 @@
+nghttp2 (1.53.0-1) unstable; urgency=medium
+
+  * New upstream version 1.53.9
+
+ -- Tomasz Buchert <tomasz@debian.org>  Sun, 21 May 2023 15:46:26 +0200
+
 nghttp2 (1.52.0-1) unstable; urgency=medium
 
   * New upstream release 1.52.0
diff -pruN 1.52.0-1/doc/h2load.1 1.53.0-1/doc/h2load.1
--- 1.52.0-1/doc/h2load.1	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/doc/h2load.1	2023-05-10 08:27:53.000000000 +0000
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "H2LOAD" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
+.TH "H2LOAD" "1" "May 10, 2023" "1.53.0" "nghttp2"
 .SH NAME
 h2load \- HTTP/2 benchmarking tool
 .SH SYNOPSIS
@@ -246,7 +246,7 @@ mutually exclusive.
 Specify URI from which the scheme, host and port will be
 used  for  all requests.   The  base  URI overrides  all
 values  defined either  at  the command  line or  inside
-input files.  If argument  starts with "unix:", then the
+input files.  If argument  starts with \(dqunix:\(dq, then the
 rest  of the  argument will  be treated  as UNIX  domain
 socket path.   The connection is made  through that path
 instead of TCP.   In this case, scheme  is inferred from
@@ -305,9 +305,9 @@ to buffering.  Status code is \-1 for fa
 .B \-\-qlog\-file\-base=<PATH>
 Enable qlog output and specify base file name for qlogs.
 Qlog is emitted  for each connection.  For  a given base
-name   "base",    each   output   file    name   becomes
-"base.M.N.sqlog" where M is worker ID and N is client ID
-(e.g. "base.0.3.sqlog").  Only effective in QUIC runs.
+name   \(dqbase\(dq,    each   output   file    name   becomes
+\(dqbase.M.N.sqlog\(dq where M is worker ID and N is client ID
+(e.g. \(dqbase.0.3.sqlog\(dq).  Only effective in QUIC runs.
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -408,7 +408,7 @@ The number of status code h2load receive
 .INDENT 7.0
 .TP
 .B total
-The number of bytes received from the server "on the wire".  If
+The number of bytes received from the server \(dqon the wire\(dq.  If
 requests were made via TLS, this value is the number of decrypted
 bytes.
 .TP
diff -pruN 1.52.0-1/doc/mkapiref.py 1.53.0-1/doc/mkapiref.py
--- 1.52.0-1/doc/mkapiref.py	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/doc/mkapiref.py	2023-05-10 08:27:53.000000000 +0000
@@ -307,7 +307,6 @@ def arg_repl(matchobj):
 def transform_content(content):
     content = re.sub(r'^\s+\* ?', '', content)
     content = re.sub(r'\|([^\s|]+)\|', arg_repl, content)
-    content = re.sub(r':enum:', ':macro:', content)
     return content
 
 if __name__ == '__main__':
diff -pruN 1.52.0-1/doc/nghttp.1 1.53.0-1/doc/nghttp.1
--- 1.52.0-1/doc/nghttp.1	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/doc/nghttp.1	2023-05-10 08:27:53.000000000 +0000
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "NGHTTP" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
+.TH "NGHTTP" "1" "May 10, 2023" "1.53.0" "nghttp2"
 .SH NAME
 nghttp \- HTTP/2 client
 .SH SYNOPSIS
@@ -280,7 +280,7 @@ the easy way to test out the dependency
 implementation.
 .sp
 When connection is established, nghttp sends 5 PRIORITY frames to idle
-streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
+streams 3, 5, 7, 9 and 11 to create \(dqanchor\(dq nodes in dependency
 tree:
 .INDENT 0.0
 .INDENT 3.5
@@ -320,8 +320,8 @@ URI given in command\-line as html, and
 it.  When requesting those resources, nghttp uses dependency according
 to its resource type.
 .sp
-For CSS, and Javascript files inside "head" element, they depend on
-stream 3 with the weight 2.  The Javascript files outside "head"
+For CSS, and Javascript files inside \(dqhead\(dq element, they depend on
+stream 3 with the weight 2.  The Javascript files outside \(dqhead\(dq
 element depend on stream 5 with the weight 2.  The mages depend on
 stream 11 with the weight 12.  The other resources (e.g., icon) depend
 on stream 11 with the weight 2.
diff -pruN 1.52.0-1/doc/nghttpd.1 1.53.0-1/doc/nghttpd.1
--- 1.52.0-1/doc/nghttpd.1	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/doc/nghttpd.1	2023-05-10 08:27:53.000000000 +0000
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "NGHTTPD" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
+.TH "NGHTTPD" "1" "May 10, 2023" "1.53.0" "nghttp2"
 .SH NAME
 nghttpd \- HTTP/2 server
 .SH SYNOPSIS
diff -pruN 1.52.0-1/doc/nghttpx.1 1.53.0-1/doc/nghttpx.1
--- 1.52.0-1/doc/nghttpx.1	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/doc/nghttpx.1	2023-05-10 08:27:53.000000000 +0000
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "NGHTTPX" "1" "Feb 13, 2023" "1.52.0" "nghttp2"
+.TH "NGHTTPX" "1" "May 10, 2023" "1.53.0" "nghttp2"
 .SH NAME
 nghttpx \- HTTP/2 proxy
 .SH SYNOPSIS
@@ -40,13 +40,13 @@ A reverse proxy for HTTP/3, HTTP/2, and
 .TP
 .B <PRIVATE_KEY>
 Set  path  to  server\(aqs private  key.   Required  unless
-"no\-tls" parameter is used in \fI\%\-\-frontend\fP option.
+\(dqno\-tls\(dq parameter is used in \fI\%\-\-frontend\fP option.
 .UNINDENT
 .INDENT 0.0
 .TP
 .B <CERT>
 Set  path  to  server\(aqs  certificate.   Required  unless
-"no\-tls"  parameter is  used in  \fI\%\-\-frontend\fP option.   To
+\(dqno\-tls\(dq  parameter is  used in  \fI\%\-\-frontend\fP option.   To
 make OCSP stapling work, this must be an absolute path.
 .UNINDENT
 .SH OPTIONS
@@ -59,56 +59,56 @@ The options are categorized into several
 Set  backend  host  and   port.   The  multiple  backend
 addresses are  accepted by repeating this  option.  UNIX
 domain socket  can be  specified by prefixing  path name
-with "unix:" (e.g., unix:/var/run/backend.sock).
+with \(dqunix:\(dq (e.g., unix:/var/run/backend.sock).
 .sp
 Optionally, if <PATTERN>s are given, the backend address
 is  only  used  if  request matches  the  pattern.   The
 pattern  matching is  closely  designed  to ServeMux  in
 net/http package of  Go programming language.  <PATTERN>
 consists of  path, host +  path or just host.   The path
-must start  with "\fI/\fP".  If  it ends with "\fI/\fP",  it matches
+must start  with \(dq\fI/\fP\(dq.  If  it ends with \(dq\fI/\fP\(dq,  it matches
 all  request path  in  its subtree.   To  deal with  the
 request  to the  directory without  trailing slash,  the
-path which ends  with "\fI/\fP" also matches  the request path
-which  only  lacks  trailing  \(aq\fI/\fP\(aq  (e.g.,  path  "\fI/foo/\fP"
-matches request path  "\fI/foo\fP").  If it does  not end with
-"\fI/\fP", it  performs exact match against  the request path.
+path which ends  with \(dq\fI/\fP\(dq also matches  the request path
+which  only  lacks  trailing  \(aq\fI/\fP\(aq  (e.g.,  path  \(dq\fI/foo/\fP\(dq
+matches request path  \(dq\fI/foo\fP\(dq).  If it does  not end with
+\(dq\fI/\fP\(dq, it  performs exact match against  the request path.
 If  host  is given,  it  performs  a match  against  the
 request host.   For a  request received on  the frontend
-listener with  "sni\-fwd" parameter enabled, SNI  host is
+listener with  \(dqsni\-fwd\(dq parameter enabled, SNI  host is
 used instead of a request host.  If host alone is given,
-"\fI/\fP" is  appended to it,  so that it matches  all request
-paths  under the  host  (e.g., specifying  "nghttp2.org"
-equals  to "nghttp2.org/").   CONNECT method  is treated
+\(dq\fI/\fP\(dq is  appended to it,  so that it matches  all request
+paths  under the  host  (e.g., specifying  \(dqnghttp2.org\(dq
+equals  to \(dqnghttp2.org/\(dq).   CONNECT method  is treated
 specially.  It  does not have  path, and we  don\(aqt allow
 empty path.  To workaround  this, we assume that CONNECT
-method has "\fI/\fP" as path.
+method has \(dq\fI/\fP\(dq as path.
 .sp
 Patterns with  host take  precedence over  patterns with
 just path.   Then, longer patterns take  precedence over
 shorter ones.
 .sp
-Host  can  include "*"  in  the  left most  position  to
+Host  can  include \(dq*\(dq  in  the  left most  position  to
 indicate  wildcard match  (only suffix  match is  done).
-The "*" must match at least one character.  For example,
-host    pattern    "*.nghttp2.org"    matches    against
-"www.nghttp2.org"  and  "git.ngttp2.org", but  does  not
-match  against  "nghttp2.org".   The exact  hosts  match
+The \(dq*\(dq must match at least one character.  For example,
+host    pattern    \(dq*.nghttp2.org\(dq    matches    against
+\(dqwww.nghttp2.org\(dq  and  \(dqgit.ngttp2.org\(dq, but  does  not
+match  against  \(dqnghttp2.org\(dq.   The exact  hosts  match
 takes precedence over the wildcard hosts match.
 .sp
-If path  part ends with  "*", it is treated  as wildcard
+If path  part ends with  \(dq*\(dq, it is treated  as wildcard
 path.  The  wildcard path  behaves differently  from the
 normal path.  For normal path,  match is made around the
-boundary of path component  separator,"\fI/\fP".  On the other
+boundary of path component  separator,\(dq\fI/\fP\(dq.  On the other
 hand, the wildcard  path does not take  into account the
 path component  separator.  All paths which  include the
-wildcard  path  without  last  "*" as  prefix,  and  are
-strictly longer than wildcard  path without last "*" are
-matched.  "*"  must match  at least one  character.  For
-example,  the   pattern  "\fI/foo*\fP"  matches   "\fI/foo/\fP"  and
-"\fI/foobar\fP".  But it does not match "\fI/foo\fP", or "\fI/fo\fP".
+wildcard  path  without  last  \(dq*\(dq as  prefix,  and  are
+strictly longer than wildcard  path without last \(dq*\(dq are
+matched.  \(dq*\(dq  must match  at least one  character.  For
+example,  the   pattern  \(dq\fI/foo*\fP\(dq  matches   \(dq\fI/foo/\fP\(dq  and
+\(dq\fI/foobar\fP\(dq.  But it does not match \(dq\fI/foo\fP\(dq, or \(dq\fI/fo\fP\(dq.
 .sp
-If <PATTERN> is omitted or  empty string, "\fI/\fP" is used as
+If <PATTERN> is omitted or  empty string, \(dq\fI/\fP\(dq is used as
 pattern,  which  matches  all request  paths  (catch\-all
 pattern).  The catch\-all backend must be given.
 .sp
@@ -116,16 +116,16 @@ When doing  a match, nghttpx made  some
 pattern, request host and path.  For host part, they are
 converted to lower case.  For path part, percent\-encoded
 unreserved characters  defined in RFC 3986  are decoded,
-and any  dot\-segments (".."  and ".")   are resolved and
+and any  dot\-segments (\(dq..\(dq  and \(dq.\(dq)   are resolved and
 removed.
 .sp
 For   example,   \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org/httpbin/\(aq
-matches the  request host "nghttp2.org" and  the request
-path "\fI/httpbin/get\fP", but does not match the request host
-"nghttp2.org" and the request path "\fI/index.html\fP".
+matches the  request host \(dqnghttp2.org\(dq and  the request
+path \(dq\fI/httpbin/get\fP\(dq, but does not match the request host
+\(dqnghttp2.org\(dq and the request path \(dq\fI/index.html\fP\(dq.
 .sp
 The  multiple <PATTERN>s  can  be specified,  delimiting
-them            by           ":".             Specifying
+them            by           \(dq:\(dq.             Specifying
 \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org:www.nghttp2.org\(aq  has  the
 same  effect  to specify  \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org\(aq
 and \fI\%\-b\fP\(aq127.0.0.1,8080;www.nghttp2.org\(aq.
@@ -134,45 +134,45 @@ The backend addresses sharing same <PATT
 together forming  load balancing  group.
 .sp
 Several parameters <PARAM> are accepted after <PATTERN>.
-The  parameters are  delimited  by  ";".  The  available
-parameters       are:      "proto=<PROTO>",       "tls",
-"sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
-"affinity=<METHOD>",    "dns",    "redirect\-if\-not\-tls",
-"upgrade\-scheme",                        "mruby=<PATH>",
-"read\-timeout=<DURATION>",   "write\-timeout=<DURATION>",
-"group=<GROUP>",  "group\-weight=<N>", "weight=<N>",  and
-"dnf".    The  parameter   consists   of  keyword,   and
-optionally followed by "="  and value.  For example, the
-parameter "proto=h2" consists of the keyword "proto" and
-value "h2".  The parameter "tls" consists of the keyword
-"tls"  without value.   Each parameter  is described  as
+The  parameters are  delimited  by  \(dq;\(dq.  The  available
+parameters       are:      \(dqproto=<PROTO>\(dq,       \(dqtls\(dq,
+\(dqsni=<SNI_HOST>\(dq,         \(dqfall=<N>\(dq,        \(dqrise=<N>\(dq,
+\(dqaffinity=<METHOD>\(dq,    \(dqdns\(dq,    \(dqredirect\-if\-not\-tls\(dq,
+\(dqupgrade\-scheme\(dq,                        \(dqmruby=<PATH>\(dq,
+\(dqread\-timeout=<DURATION>\(dq,   \(dqwrite\-timeout=<DURATION>\(dq,
+\(dqgroup=<GROUP>\(dq,  \(dqgroup\-weight=<N>\(dq, \(dqweight=<N>\(dq,  and
+\(dqdnf\(dq.    The  parameter   consists   of  keyword,   and
+optionally followed by \(dq=\(dq  and value.  For example, the
+parameter \(dqproto=h2\(dq consists of the keyword \(dqproto\(dq and
+value \(dqh2\(dq.  The parameter \(dqtls\(dq consists of the keyword
+\(dqtls\(dq  without value.   Each parameter  is described  as
 follows.
 .sp
 The backend application protocol  can be specified using
-optional  "proto"   parameter,  and   in  the   form  of
-"proto=<PROTO>".  <PROTO> should be one of the following
-list  without  quotes:  "h2", "http/1.1".   The  default
-value of <PROTO> is  "http/1.1".  Note that usually "h2"
+optional  \(dqproto\(dq   parameter,  and   in  the   form  of
+\(dqproto=<PROTO>\(dq.  <PROTO> should be one of the following
+list  without  quotes:  \(dqh2\(dq, \(dqhttp/1.1\(dq.   The  default
+value of <PROTO> is  \(dqhttp/1.1\(dq.  Note that usually \(dqh2\(dq
 refers to HTTP/2  over TLS.  But in this  option, it may
-mean HTTP/2  over cleartext TCP unless  "tls" keyword is
+mean HTTP/2  over cleartext TCP unless  \(dqtls\(dq keyword is
 used (see below).
 .sp
-TLS  can   be  enabled  by  specifying   optional  "tls"
+TLS  can   be  enabled  by  specifying   optional  \(dqtls\(dq
 parameter.  TLS is not enabled by default.
 .sp
-With "sni=<SNI_HOST>" parameter, it can override the TLS
+With \(dqsni=<SNI_HOST>\(dq parameter, it can override the TLS
 SNI  field  value  with  given  <SNI_HOST>.   This  will
 default to the backend <HOST> name
 .sp
 The  feature  to detect  whether  backend  is online  or
-offline can be enabled  using optional "fall" and "rise"
-parameters.   Using  "fall=<N>"  parameter,  if  nghttpx
+offline can be enabled  using optional \(dqfall\(dq and \(dqrise\(dq
+parameters.   Using  \(dqfall=<N>\(dq  parameter,  if  nghttpx
 cannot connect  to a  this backend <N>  times in  a row,
 this  backend  is  assumed  to be  offline,  and  it  is
 excluded from load balancing.  If <N> is 0, this backend
 never  be excluded  from load  balancing whatever  times
 nghttpx cannot connect  to it, and this  is the default.
-There is  also "rise=<N>" parameter.  After  backend was
+There is  also \(dqrise=<N>\(dq parameter.  After  backend was
 excluded from load balancing group, nghttpx periodically
 attempts to make a connection to the failed backend, and
 if the  connection is made  successfully <N> times  in a
@@ -182,80 +182,80 @@ backend  is permanently  offline, once
 state, and this is the default behaviour.
 .sp
 The     session     affinity    is     enabled     using
-"affinity=<METHOD>"  parameter.   If  "ip" is  given  in
+\(dqaffinity=<METHOD>\(dq  parameter.   If  \(dqip\(dq is  given  in
 <METHOD>, client  IP based session affinity  is enabled.
-If "cookie"  is given in <METHOD>,  cookie based session
-affinity is  enabled.  If  "none" is given  in <METHOD>,
+If \(dqcookie\(dq  is given in <METHOD>,  cookie based session
+affinity is  enabled.  If  \(dqnone\(dq is given  in <METHOD>,
 session affinity  is disabled, and this  is the default.
 The session  affinity is  enabled per <PATTERN>.   If at
-least  one backend  has  "affinity"  parameter, and  its
-<METHOD> is not "none",  session affinity is enabled for
+least  one backend  has  \(dqaffinity\(dq  parameter, and  its
+<METHOD> is not \(dqnone\(dq,  session affinity is enabled for
 all backend  servers sharing the same  <PATTERN>.  It is
-advised  to  set  "affinity" parameter  to  all  backend
+advised  to  set  \(dqaffinity\(dq parameter  to  all  backend
 explicitly if session affinity  is desired.  The session
 affinity  may   break  if   one  of  the   backend  gets
 unreachable,  or   backend  settings  are   reloaded  or
 replaced by API.
 .sp
-If   "affinity=cookie"    is   used,    the   additional
+If   \(dqaffinity=cookie\(dq    is   used,    the   additional
 configuration                is                required.
-"affinity\-cookie\-name=<NAME>" must be  used to specify a
+\(dqaffinity\-cookie\-name=<NAME>\(dq must be  used to specify a
 name     of     cookie      to     use.      Optionally,
-"affinity\-cookie\-path=<PATH>" can  be used to  specify a
+\(dqaffinity\-cookie\-path=<PATH>\(dq can  be used to  specify a
 path   which   cookie    is   applied.    The   optional
-"affinity\-cookie\-secure=<SECURE>"  controls  the  Secure
-attribute of a cookie.  The default value is "auto", and
+\(dqaffinity\-cookie\-secure=<SECURE>\(dq  controls  the  Secure
+attribute of a cookie.  The default value is \(dqauto\(dq, and
 the Secure attribute is  determined by a request scheme.
-If a request scheme is "https", then Secure attribute is
-set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
+If a request scheme is \(dqhttps\(dq, then Secure attribute is
+set.  Otherwise, it  is not set.  If  <SECURE> is \(dqyes\(dq,
 the  Secure attribute  is  always set.   If <SECURE>  is
-"no",   the   Secure   attribute  is   always   omitted.
-"affinity\-cookie\-stickiness=<STICKINESS>"       controls
+\(dqno\(dq,   the   Secure   attribute  is   always   omitted.
+\(dqaffinity\-cookie\-stickiness=<STICKINESS>\(dq       controls
 stickiness  of   this  affinity.   If   <STICKINESS>  is
-"loose", removing or adding a backend server might break
+\(dqloose\(dq, removing or adding a backend server might break
 the affinity  and the  request might  be forwarded  to a
-different backend server.   If <STICKINESS> is "strict",
+different backend server.   If <STICKINESS> is \(dqstrict\(dq,
 removing the designated  backend server breaks affinity,
 but adding  new backend server does  not cause breakage.
 If  the designated  backend server  becomes unavailable,
 new backend server is chosen  as if the request does not
 have  an  affinity  cookie.   <STICKINESS>  defaults  to
-"loose".
+\(dqloose\(dq.
 .sp
 By default, name resolution of backend host name is done
-at  start  up,  or reloading  configuration.   If  "dns"
+at  start  up,  or reloading  configuration.   If  \(dqdns\(dq
 parameter   is  given,   name  resolution   takes  place
 dynamically.  This is useful  if backend address changes
-frequently.   If  "dns"  is given,  name  resolution  of
+frequently.   If  \(dqdns\(dq  is given,  name  resolution  of
 backend   host   name   at  start   up,   or   reloading
 configuration is skipped.
 .sp
-If "redirect\-if\-not\-tls" parameter  is used, the matched
+If \(dqredirect\-if\-not\-tls\(dq parameter  is used, the matched
 backend  requires   that  frontend  connection   is  TLS
 encrypted.  If it isn\(aqt, nghttpx responds to the request
 with 308  status code, and  https URI the  client should
 use instead  is included in Location  header field.  The
 port number in  redirect URI is 443 by  default, and can
 be  changed using  \fI\%\-\-redirect\-https\-port\fP option.   If at
-least one  backend has  "redirect\-if\-not\-tls" parameter,
+least one  backend has  \(dqredirect\-if\-not\-tls\(dq parameter,
 this feature is enabled  for all backend servers sharing
 the   same   <PATTERN>.    It    is   advised   to   set
-"redirect\-if\-no\-tls"    parameter   to    all   backends
+\(dqredirect\-if\-no\-tls\(dq    parameter   to    all   backends
 explicitly if this feature is desired.
 .sp
-If "upgrade\-scheme"  parameter is used along  with "tls"
+If \(dqupgrade\-scheme\(dq  parameter is used along  with \(dqtls\(dq
 parameter, HTTP/2 :scheme pseudo header field is changed
-to "https" from "http" when forwarding a request to this
+to \(dqhttps\(dq from \(dqhttp\(dq when forwarding a request to this
 particular backend.  This is  a workaround for a backend
-server  which  requires  "https" :scheme  pseudo  header
+server  which  requires  \(dqhttps\(dq :scheme  pseudo  header
 field on TLS encrypted connection.
 .sp
-"mruby=<PATH>"  parameter  specifies  a  path  to  mruby
+\(dqmruby=<PATH>\(dq  parameter  specifies  a  path  to  mruby
 script  file  which  is  invoked when  this  pattern  is
 matched.  All backends which share the same pattern must
 have the same mruby path.
 .sp
-"read\-timeout=<DURATION>" and "write\-timeout=<DURATION>"
+\(dqread\-timeout=<DURATION>\(dq and \(dqwrite\-timeout=<DURATION>\(dq
 parameters  specify the  read and  write timeout  of the
 backend connection  when this  pattern is  matched.  All
 backends which share the same pattern must have the same
@@ -263,42 +263,42 @@ timeouts.  If these timeouts  are entire
 pattern,            \fI\%\-\-backend\-read\-timeout\fP           and
 \fI\%\-\-backend\-write\-timeout\fP are used.
 .sp
-"group=<GROUP>"  parameter specifies  the name  of group
+\(dqgroup=<GROUP>\(dq  parameter specifies  the name  of group
 this backend address belongs to.  By default, it belongs
 to  the unnamed  default group.   The name  of group  is
-unique   per   pattern.   "group\-weight=<N>"   parameter
+unique   per   pattern.   \(dqgroup\-weight=<N>\(dq   parameter
 specifies the  weight of  the group.  The  higher weight
 gets  more frequently  selected  by  the load  balancing
 algorithm.  <N> must be  [1, 256] inclusive.  The weight
 8 has 4 times more weight  than 2.  <N> must be the same
 for  all addresses  which  share the  same <GROUP>.   If
-"group\-weight" is  omitted in an address,  but the other
+\(dqgroup\-weight\(dq is  omitted in an address,  but the other
 address  which  belongs  to  the  same  group  specifies
-"group\-weight",   its    weight   is   used.     If   no
-"group\-weight"  is  specified  for  all  addresses,  the
-weight of a group becomes 1.  "group" and "group\-weight"
+\(dqgroup\-weight\(dq,   its    weight   is   used.     If   no
+\(dqgroup\-weight\(dq  is  specified  for  all  addresses,  the
+weight of a group becomes 1.  \(dqgroup\(dq and \(dqgroup\-weight\(dq
 are ignored if session affinity is enabled.
 .sp
-"weight=<N>"  parameter  specifies  the  weight  of  the
+\(dqweight=<N>\(dq  parameter  specifies  the  weight  of  the
 backend  address  inside  a  group  which  this  address
 belongs  to.  The  higher  weight  gets more  frequently
 selected by  the load balancing algorithm.   <N> must be
 [1,  256] inclusive.   The  weight 8  has  4 times  more
 weight  than weight  2.  If  this parameter  is omitted,
-weight  becomes  1.   "weight"  is  ignored  if  session
+weight  becomes  1.   \(dqweight\(dq  is  ignored  if  session
 affinity is enabled.
 .sp
-If "dnf" parameter is  specified, an incoming request is
+If \(dqdnf\(dq parameter is  specified, an incoming request is
 not forwarded to a backend  and just consumed along with
 the  request body  (actually a  backend server  never be
 contacted).  It  is expected  that the HTTP  response is
-generated by mruby  script (see "mruby=<PATH>" parameter
-above).  "dnf" is an abbreviation of "do not forward".
+generated by mruby  script (see \(dqmruby=<PATH>\(dq parameter
+above).  \(dqdnf\(dq is an abbreviation of \(dqdo not forward\(dq.
 .sp
-Since ";" and ":" are  used as delimiter, <PATTERN> must
-not contain  these characters.  In order  to include ":"
-in  <PATTERN>,  one  has  to  specify  "%3A"  (which  is
-percent\-encoded  from of  ":") instead.   Since ";"  has
+Since \(dq;\(dq and \(dq:\(dq are  used as delimiter, <PATTERN> must
+not contain  these characters.  In order  to include \(dq:\(dq
+in  <PATTERN>,  one  has  to  specify  \(dq%3A\(dq  (which  is
+percent\-encoded  from of  \(dq:\(dq) instead.   Since \(dq;\(dq  has
 special  meaning  in shell,  the  option  value must  be
 quoted.
 .sp
@@ -310,23 +310,23 @@ Default: \fB127.0.0.1,80\fP
 Set  frontend  host and  port.   If  <HOST> is  \(aq*\(aq,  it
 assumes  all addresses  including  both  IPv4 and  IPv6.
 UNIX domain  socket can  be specified by  prefixing path
-name  with  "unix:" (e.g.,  unix:/var/run/nghttpx.sock).
+name  with  \(dqunix:\(dq (e.g.,  unix:/var/run/nghttpx.sock).
 This  option can  be used  multiple times  to listen  to
 multiple addresses.
 .sp
 This option  can take  0 or  more parameters,  which are
-described  below.   Note   that  "api"  and  "healthmon"
+described  below.   Note   that  \(dqapi\(dq  and  \(dqhealthmon\(dq
 parameters are mutually exclusive.
 .sp
-Optionally, TLS  can be disabled by  specifying "no\-tls"
+Optionally, TLS  can be disabled by  specifying \(dqno\-tls\(dq
 parameter.  TLS is enabled by default.
 .sp
-If "sni\-fwd" parameter is  used, when performing a match
+If \(dqsni\-fwd\(dq parameter is  used, when performing a match
 to select a backend server,  SNI host name received from
 the client  is used  instead of  the request  host.  See
 \fI\%\-\-backend\fP option about the pattern match.
 .sp
-To  make this  frontend as  API endpoint,  specify "api"
+To  make this  frontend as  API endpoint,  specify \(dqapi\(dq
 parameter.   This   is  disabled  by  default.    It  is
 important  to  limit the  access  to  the API  frontend.
 Otherwise, someone  may change  the backend  server, and
@@ -334,18 +334,18 @@ break your services,  or expose confiden
 to the outside the world.
 .sp
 To  make  this  frontend  as  health  monitor  endpoint,
-specify  "healthmon"  parameter.   This is  disabled  by
+specify  \(dqhealthmon\(dq  parameter.   This is  disabled  by
 default.  Any  requests which come through  this address
 are replied with 200 HTTP status, without no body.
 .sp
 To accept  PROXY protocol  version 1  and 2  on frontend
-connection,  specify  "proxyproto" parameter.   This  is
+connection,  specify  \(dqproxyproto\(dq parameter.   This  is
 disabled by default.
 .sp
-To  receive   HTTP/3  (QUIC)  traffic,   specify  "quic"
+To  receive   HTTP/3  (QUIC)  traffic,   specify  \(dqquic\(dq
 parameter.  It  makes nghttpx listen on  UDP port rather
-than  TCP   port.   UNIX   domain  socket,   "api",  and
-"healthmon"  parameters  cannot   be  used  with  "quic"
+than  TCP   port.   UNIX   domain  socket,   \(dqapi\(dq,  and
+\(dqhealthmon\(dq  parameters  cannot   be  used  with  \(dqquic\(dq
 parameter.
 .sp
 Default: \fB*,3000\fP
@@ -361,9 +361,9 @@ Default: \fB65536\fP
 .TP
 .B \-\-backend\-address\-family=(auto|IPv4|IPv6)
 Specify  address  family  of  backend  connections.   If
-"auto" is given, both IPv4  and IPv6 are considered.  If
-"IPv4" is  given, only  IPv4 address is  considered.  If
-"IPv6" is given, only IPv6 address is considered.
+\(dqauto\(dq is given, both IPv4  and IPv6 are considered.  If
+\(dqIPv4\(dq is  given, only  IPv4 address is  considered.  If
+\(dqIPv6\(dq is given, only IPv6 address is considered.
 .sp
 Default: \fBauto\fP
 .UNINDENT
@@ -538,7 +538,7 @@ Default: \fB128K\fP
 .INDENT 0.0
 .TP
 .B \-\-fastopen=<N>
-Enables  "TCP Fast  Open" for  the listening  socket and
+Enables  \(dqTCP Fast  Open\(dq for  the listening  socket and
 limits the  maximum length for the  queue of connections
 that have not yet completed the three\-way handshake.  If
 value is 0 then fast open is disabled.
@@ -664,7 +664,7 @@ Default: \fB10s\fP
 .TP
 .B \-\-backend\-max\-backoff=<DURATION>
 Specify  maximum backoff  interval.  This  is used  when
-doing health  check against offline backend  (see "fail"
+doing health  check against offline backend  (see \(dqfail\(dq
 parameter  in \fI\%\-\-backend\fP  option).   It is  also used  to
 limit  the  maximum   interval  to  temporarily  disable
 backend  when nghttpx  failed to  connect to  it.  These
@@ -766,9 +766,9 @@ used  multiple  times.   To  make  OCSP
 <CERTPATH> must be absolute path.
 .sp
 Additional parameter  can be specified in  <PARAM>.  The
-available <PARAM> is "sct\-dir=<DIR>".
+available <PARAM> is \(dqsct\-dir=<DIR>\(dq.
 .sp
-"sct\-dir=<DIR>"  specifies the  path to  directory which
+\(dqsct\-dir=<DIR>\(dq  specifies the  path to  directory which
 contains        *.sct        files        for        TLS
 signed_certificate_timestamp extension (RFC 6962).  This
 feature   requires   OpenSSL   >=   1.0.2.    See   also
@@ -833,7 +833,7 @@ done in  case\-insensitive manner.  The
 \fI\%\-\-tls\-min\-proto\-version\fP and  \fI\%\-\-tls\-max\-proto\-version\fP are
 enabled.  If the protocol list advertised by client does
 not  overlap  this range,  you  will  receive the  error
-message "unknown protocol".  If a protocol version lower
+message \(dqunknown protocol\(dq.  If a protocol version lower
 than TLSv1.2 is specified, make sure that the compatible
 ciphers are  included in \fI\%\-\-ciphers\fP option.   The default
 cipher  list  only   includes  ciphers  compatible  with
@@ -850,7 +850,7 @@ done in  case\-insensitive manner.  The
 \fI\%\-\-tls\-min\-proto\-version\fP and  \fI\%\-\-tls\-max\-proto\-version\fP are
 enabled.  If the protocol list advertised by client does
 not  overlap  this range,  you  will  receive the  error
-message "unknown protocol".  The available versions are:
+message \(dqunknown protocol\(dq.  The available versions are:
 TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
 .sp
 Default: \fBTLSv1.3\fP
@@ -891,18 +891,18 @@ ticket key generator is required.  nghtt
 ticket  keys  from  memcached, and  use  them,  possibly
 replacing current set  of keys.  It is up  to extern TLS
 ticket  key generator  to rotate  keys frequently.   See
-"TLS SESSION  TICKET RESUMPTION" section in  manual page
+\(dqTLS SESSION  TICKET RESUMPTION\(dq section in  manual page
 to know the data format in memcached entry.  Optionally,
 memcached  connection  can  be  encrypted  with  TLS  by
-specifying "tls" parameter.
+specifying \(dqtls\(dq parameter.
 .UNINDENT
 .INDENT 0.0
 .TP
 .B \-\-tls\-ticket\-key\-memcached\-address\-family=(auto|IPv4|IPv6)
 Specify address  family of memcached connections  to get
-TLS ticket keys.  If "auto" is given, both IPv4 and IPv6
-are considered.   If "IPv4" is given,  only IPv4 address
-is considered.  If "IPv6" is given, only IPv6 address is
+TLS ticket keys.  If \(dqauto\(dq is given, both IPv4 and IPv6
+are considered.   If \(dqIPv4\(dq is given,  only IPv4 address
+is considered.  If \(dqIPv6\(dq is given, only IPv6 address is
 considered.
 .sp
 Default: \fBauto\fP
@@ -920,7 +920,7 @@ Default: \fB10m\fP
 Set  maximum   number  of  consecutive   retries  before
 abandoning TLS ticket key  retrieval.  If this number is
 reached,  the  attempt  is considered  as  failure,  and
-"failure" count  is incremented by 1,  which contributed
+\(dqfailure\(dq count  is incremented by 1,  which contributed
 to            the            value            controlled
 \fI\%\-\-tls\-ticket\-key\-memcached\-max\-fail\fP option.
 .sp
@@ -993,16 +993,16 @@ Disable OCSP stapling.
 Specify  address of  memcached server  to store  session
 cache.   This  enables   shared  session  cache  between
 multiple   nghttpx  instances.    Optionally,  memcached
-connection can be encrypted with TLS by specifying "tls"
+connection can be encrypted with TLS by specifying \(dqtls\(dq
 parameter.
 .UNINDENT
 .INDENT 0.0
 .TP
 .B \-\-tls\-session\-cache\-memcached\-address\-family=(auto|IPv4|IPv6)
 Specify address family of memcached connections to store
-session cache.  If  "auto" is given, both  IPv4 and IPv6
-are considered.   If "IPv4" is given,  only IPv4 address
-is considered.  If "IPv6" is given, only IPv6 address is
+session cache.  If  \(dqauto\(dq is given, both  IPv4 and IPv6
+are considered.   If \(dqIPv4\(dq is given,  only IPv4 address
+is considered.  If \(dqIPv6\(dq is given, only IPv6 address is
 considered.
 .sp
 Default: \fBauto\fP
@@ -1113,7 +1113,7 @@ By  default,   except  for  QUIC   conne
 postpones forwarding  HTTP requests sent in  early data,
 including  those  sent in  partially  in  it, until  TLS
 handshake  finishes.  If  all backend  server recognizes
-"Early\-Data"  header  field,  using  this  option  makes
+\(dqEarly\-Data\(dq  header  field,  using  this  option  makes
 nghttpx  not postpone  forwarding request  and get  full
 potential of 0\-RTT data.
 .UNINDENT
@@ -1274,7 +1274,7 @@ Default: \fB4K\fP
 .INDENT 0.0
 .TP
 .B (default mode)
-Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no\-tls"
+Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   \(dqno\-tls\(dq
 parameter is  used in  \fI\%\-\-frontend\fP option,  accept HTTP/2
 and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
 connection  can  be  upgraded  to  HTTP/2  through  HTTP
@@ -1365,16 +1365,16 @@ $tls_protocol: protocol for SSL/TLS conn
 .IP \(bu 2
 $tls_session_id: session ID for SSL/TLS connection.
 .IP \(bu 2
-$tls_session_reused:  "r"   if  SSL/TLS   session  was
-reused.  Otherwise, "."
+$tls_session_reused:  \(dqr\(dq   if  SSL/TLS   session  was
+reused.  Otherwise, \(dq.\(dq
 .IP \(bu 2
 $tls_sni: SNI server name for SSL/TLS connection.
 .IP \(bu 2
 $backend_host:  backend  host   used  to  fulfill  the
-request.  "\-" if backend host is not available.
+request.  \(dq\-\(dq if backend host is not available.
 .IP \(bu 2
 $backend_port:  backend  port   used  to  fulfill  the
-request.  "\-" if backend host is not available.
+request.  \(dq\-\(dq if backend host is not available.
 .IP \(bu 2
 $method: HTTP method
 .IP \(bu 2
@@ -1389,10 +1389,10 @@ $protocol_version:   HTTP  version   (e.
 HTTP/2)
 .UNINDENT
 .sp
-The  variable  can  be  enclosed  by  "{"  and  "}"  for
+The  variable  can  be  enclosed  by  \(dq{\(dq  and  \(dq}\(dq  for
 disambiguation (e.g., ${remote_addr}).
 .sp
-Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP
+Default: \fB$remote_addr \- \- [$time_local] \(dq$request\(dq $status $body_bytes_sent \(dq$http_referer\(dq \(dq$http_user_agent\(dq\fP
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -1456,8 +1456,8 @@ client requests.
 .B \-\-add\-forwarded=<LIST>
 Append RFC  7239 Forwarded header field  with parameters
 specified in comma delimited list <LIST>.  The supported
-parameters  are "by",  "for", "host",  and "proto".   By
-default,  the value  of  "by" and  "for" parameters  are
+parameters  are \(dqby\(dq,  \(dqfor\(dq, \(dqhost\(dq,  and \(dqproto\(dq.   By
+default,  the value  of  \(dqby\(dq and  \(dqfor\(dq parameters  are
 obfuscated     string.     See     \fI\%\-\-forwarded\-by\fP    and
 \fI\%\-\-forwarded\-for\fP options respectively.  Note that nghttpx
 does  not  translate non\-standard  X\-Forwarded\-*  header
@@ -1472,15 +1472,15 @@ requests.
 .INDENT 0.0
 .TP
 .B \-\-forwarded\-by=(obfuscated|ip|<VALUE>)
-Specify the parameter value sent out with "by" parameter
-of Forwarded  header field.   If "obfuscated"  is given,
-the string is randomly generated at startup.  If "ip" is
+Specify the parameter value sent out with \(dqby\(dq parameter
+of Forwarded  header field.   If \(dqobfuscated\(dq  is given,
+the string is randomly generated at startup.  If \(dqip\(dq is
 given,   the  interface   address  of   the  connection,
-including port number, is  sent with "by" parameter.  In
-case of UNIX domain  socket, "localhost" is used instead
+including port number, is  sent with \(dqby\(dq parameter.  In
+case of UNIX domain  socket, \(dqlocalhost\(dq is used instead
 of address and  port.  User can also  specify the static
 obfuscated string.  The limitation is that it must start
-with   "_",  and   only   consists   of  character   set
+with   \(dq_\(dq,  and   only   consists   of  character   set
 [A\-Za\-z0\-9._\-], as described in RFC 7239.
 .sp
 Default: \fBobfuscated\fP
@@ -1488,13 +1488,13 @@ Default: \fBobfuscated\fP
 .INDENT 0.0
 .TP
 .B \-\-forwarded\-for=(obfuscated|ip)
-Specify  the   parameter  value  sent  out   with  "for"
-parameter of Forwarded header field.  If "obfuscated" is
+Specify  the   parameter  value  sent  out   with  \(dqfor\(dq
+parameter of Forwarded header field.  If \(dqobfuscated\(dq is
 given, the string is  randomly generated for each client
-connection.  If "ip" is given, the remote client address
+connection.  If \(dqip\(dq is given, the remote client address
 of  the connection,  without port  number, is  sent with
-"for"  parameter.   In  case   of  UNIX  domain  socket,
-"localhost" is used instead of address.
+\(dqfor\(dq  parameter.   In  case   of  UNIX  domain  socket,
+\(dqlocalhost\(dq is used instead of address.
 .sp
 Default: \fBobfuscated\fP
 .UNINDENT
@@ -1534,7 +1534,7 @@ they  are treated  as  nothing is  speci
 advertised  in alt\-svc  header  field  only in  HTTP/1.1
 frontend.   This option  can be  used multiple  times to
 specify multiple alternative services.
-Example: \fI\%\-\-altsvc\fP="h2,443,,,ma=3600; persist=1"
+Example: \fI\%\-\-altsvc\fP=\(dqh2,443,,,ma=3600; persist=1\(dq
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -1549,7 +1549,7 @@ Specify additional header field to add t
 set.  This  option just  appends header field  and won\(aqt
 replace anything  already set.  This option  can be used
 several  times   to  specify  multiple   header  fields.
-Example: \fI\%\-\-add\-request\-header\fP="foo: bar"
+Example: \fI\%\-\-add\-request\-header\fP=\(dqfoo: bar\(dq
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -1558,7 +1558,7 @@ Specify  additional  header  field to  a
 header set.   This option just appends  header field and
 won\(aqt replace anything already  set.  This option can be
 used several  times to  specify multiple  header fields.
-Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
+Example: \fI\%\-\-add\-response\-header\fP=\(dqfoo: bar\(dq
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -1604,7 +1604,7 @@ Default: \fB500\fP
 Set file path  to custom error page  served when nghttpx
 originally  generates  HTTP  error status  code  <CODE>.
 <CODE> must be greater than or equal to 400, and at most
-599.  If "*"  is used instead of <CODE>,  it matches all
+599.  If \(dq*\(dq  is used instead of <CODE>,  it matches all
 HTTP  status  code.  If  error  status  code comes  from
 backend server, the custom error pages are not used.
 .UNINDENT
@@ -1627,7 +1627,7 @@ regardless of this option.
 .B \-\-redirect\-https\-port=<PORT>
 Specify the port number which appears in Location header
 field  when  redirect  to  HTTPS  URI  is  made  due  to
-"redirect\-if\-not\-tls" parameter in \fI\%\-\-backend\fP option.
+\(dqredirect\-if\-not\-tls\(dq parameter in \fI\%\-\-backend\fP option.
 .sp
 Default: \fB443\fP
 .UNINDENT
@@ -1807,7 +1807,7 @@ Default: \fB/usr/local/lib/nghttp2/reuse
 .TP
 .B \-\-frontend\-quic\-early\-data
 Enable early data on frontend QUIC connections.  nghttpx
-sends "Early\-Data" header field to a backend server if a
+sends \(dqEarly\-Data\(dq header field to a backend server if a
 request is received in early  data and handshake has not
 finished.  All backend servers should deal with possibly
 replayed requests.
@@ -1818,8 +1818,8 @@ replayed requests.
 Specify a  directory where  a qlog  file is  written for
 frontend QUIC  connections.  A qlog file  is created per
 each QUIC  connection.  The  file name is  ISO8601 basic
-format, followed by "\-", server Source Connection ID and
-".sqlog".
+format, followed by \(dq\-\(dq, server Source Connection ID and
+\(dq.sqlog\(dq.
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -1832,8 +1832,8 @@ NEW_TOKEN frame in the previous connecti
 .TP
 .B \-\-frontend\-quic\-congestion\-controller=<CC>
 Specify a congestion controller algorithm for a frontend
-QUIC connection.  <CC> should  be one of "cubic", "bbr",
-and "bbr2".
+QUIC connection.  <CC> should  be one of \(dqcubic\(dq, \(dqbbr\(dq,
+and \(dqbbr2\(dq.
 .sp
 Default: \fBcubic\fP
 .UNINDENT
@@ -2008,7 +2008,7 @@ instead of:
 .sp
 .nf
 .ft C
-add\-request\-header="foo: bar"
+add\-request\-header=\(dqfoo: bar\(dq
 .ft P
 .fi
 .UNINDENT
@@ -2127,7 +2127,7 @@ Link: </css/theme.css>; rel=preload
 Currently, the following restriction is applied for server push:
 .INDENT 0.0
 .IP 1. 3
-The associated stream must have method "GET" or "POST".  The
+The associated stream must have method \(dqGET\(dq or \(dqPOST\(dq.  The
 associated stream\(aqs status code must be 200.
 .UNINDENT
 .sp
@@ -2295,7 +2295,7 @@ response without forwarding request to b
 There are 2 levels of mruby script invocations: global and
 per\-pattern.  The global mruby script is set by \fI\%\-\-mruby\-file\fP
 option and is called for all requests.  The per\-pattern mruby script
-is set by "mruby" parameter in \fI\%\-b\fP option.  It is invoked for
+is set by \(dqmruby\(dq parameter in \fI\%\-b\fP option.  It is invoked for
 a request which matches the particular pattern.  The order of hook
 invocation is: global request phase hook, per\-pattern request phase
 hook, per\-pattern response phase hook, and finally global response
@@ -2362,7 +2362,7 @@ Return the current phase.
 .TP
 .B attribute [R] remote_addr
 Return IP address of a remote client.  If connection is made
-via UNIX domain socket, this returns the string "localhost".
+via UNIX domain socket, this returns the string \(dqlocalhost\(dq.
 .UNINDENT
 .INDENT 7.0
 .TP
@@ -2504,7 +2504,7 @@ not include authority component of URI.
 query component.  nghttpx makes certain normalization for
 path.  It decodes percent\-encoding for unreserved characters
 (see \fI\%https://tools.ietf.org/html/rfc3986#section\-2.3\fP), and
-resolves ".." and ".".  But it may leave characters which
+resolves \(dq..\(dq and \(dq.\(dq.  But it may leave characters which
 should be percent\-encoded as is. So be careful when comparing
 path against desired string.
 .UNINDENT
@@ -2646,7 +2646,7 @@ Modify request path:
 .ft C
 class App
   def on_req(env)
-    env.req.path = "/apps#{env.req.path}"
+    env.req.path = \(dq/apps#{env.req.path}\(dq
   end
 end
 
@@ -2667,12 +2667,12 @@ addresses:
 .ft C
 class App
   def on_req(env)
-    allowed_clients = ["127.0.0.1", "::1"]
+    allowed_clients = [\(dq127.0.0.1\(dq, \(dq::1\(dq]
 
-    if env.req.path.start_with?("/log/") &&
+    if env.req.path.start_with?(\(dq/log/\(dq) &&
        !allowed_clients.include?(env.remote_addr) then
       env.resp.status = 404
-      env.resp.return "permission denied"
+      env.resp.return \(dqpermission denied\(dq
     end
   end
 end
@@ -2686,7 +2686,7 @@ App.new
 .sp
 nghttpx exposes API endpoints to manipulate it via HTTP based API.  By
 default, API endpoint is disabled.  To enable it, add a dedicated
-frontend for API using \fI\%\-\-frontend\fP option with "api"
+frontend for API using \fI\%\-\-frontend\fP option with \(dqapi\(dq
 parameter.  All requests which come from this frontend address, will
 be treated as API request.
 .sp
@@ -2713,7 +2713,7 @@ HTTP status code
 Additionally, depending on the API endpoint, \fBdata\fP key may be
 present, and its value contains the API endpoint specific data.
 .sp
-We wrote "normally", since nghttpx may return ordinal HTML response in
+We wrote \(dqnormally\(dq, since nghttpx may return ordinal HTML response in
 some cases where the error has occurred before reaching API endpoint
 (e.g., header field is too large).
 .sp
@@ -2737,7 +2737,7 @@ connections or requests.  It also avoids
 the case with hot swapping with signals.
 .sp
 The one limitation is that only numeric IP address is allowed in
-\fI\%backend\fP in request body unless "dns" parameter
+\fI\%backend\fP in request body unless \(dqdns\(dq parameter
 is used while non numeric hostname is allowed in command\-line or
 configuration file is read using \fI\%\-\-conf\fP\&.
 .SS GET /api/v1beta1/configrevision
diff -pruN 1.52.0-1/docker/Dockerfile 1.53.0-1/docker/Dockerfile
--- 1.52.0-1/docker/Dockerfile	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/docker/Dockerfile	2023-05-10 08:27:53.000000000 +0000
@@ -15,7 +15,7 @@ RUN git clone --depth 1 -b OpenSSL_1_1_1
     cd .. && \
     rm -rf openssl
 
-RUN git clone --depth 1 -b v0.8.0 https://github.com/ngtcp2/nghttp3 && \
+RUN git clone --depth 1 -b v0.11.0 https://github.com/ngtcp2/nghttp3 && \
     cd nghttp3 && \
     autoreconf -i && \
     ./configure --enable-lib-only && \
@@ -24,7 +24,7 @@ RUN git clone --depth 1 -b v0.8.0 https:
     cd .. && \
     rm -rf nghttp3
 
-RUN git clone --depth 1 -b v0.13.1 https://github.com/ngtcp2/ngtcp2 && \
+RUN git clone --depth 1 -b v0.15.0 https://github.com/ngtcp2/ngtcp2 && \
     cd ngtcp2 && \
     autoreconf -i && \
     ./configure --enable-lib-only \
diff -pruN 1.52.0-1/go.mod 1.53.0-1/go.mod
--- 1.52.0-1/go.mod	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/go.mod	2023-05-10 08:27:53.000000000 +0000
@@ -1,26 +1,26 @@
 module github.com/nghttp2/nghttp2
 
-go 1.18
+go 1.19
 
 require (
-	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d
-	github.com/lucas-clemente/quic-go v0.30.0
+	github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746
+	github.com/quic-go/quic-go v0.33.0
 	github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20150408091349-4742878d9c90
-	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
+	golang.org/x/net v0.7.0
 )
 
 require (
 	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
 	github.com/golang/mock v1.6.0 // indirect
 	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
-	github.com/marten-seemann/qpack v0.3.0 // indirect
-	github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
-	github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
 	github.com/onsi/ginkgo/v2 v2.2.0 // indirect
-	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
-	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
-	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
-	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.12 // indirect
+	github.com/quic-go/qpack v0.4.0 // indirect
+	github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
+	github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
+	golang.org/x/crypto v0.4.0 // indirect
+	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
+	golang.org/x/mod v0.6.0 // indirect
+	golang.org/x/sys v0.5.0 // indirect
+	golang.org/x/text v0.7.0 // indirect
+	golang.org/x/tools v0.2.0 // indirect
 )
diff -pruN 1.52.0-1/go.sum 1.53.0-1/go.sum
--- 1.52.0-1/go.sum	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/go.sum	2023-05-10 08:27:53.000000000 +0000
@@ -1,5 +1,5 @@
-github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
-github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
+github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746 h1:wAIE/kN63Oig1DdOzN7O+k4AbFh2cCJoKMFXrwRJtzk=
+github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -15,19 +15,19 @@ github.com/google/go-cmp v0.5.8 h1:e6P7q
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/lucas-clemente/quic-go v0.30.0 h1:nwLW0h8ahVQ5EPTIM7uhl/stHqQDea15oRlYKZmw2O0=
-github.com/lucas-clemente/quic-go v0.30.0/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk=
-github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
-github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
-github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
-github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
-github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
-github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
 github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
 github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
 github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
+github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
+github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
+github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
+github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
+github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
+github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
+github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -36,18 +36,18 @@ github.com/tatsuhiro-t/go-nghttp2 v0.0.0
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
-golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
+golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
+golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
+golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
+golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -56,18 +56,18 @@ golang.org/x/sys v0.0.0-20191204072324-c
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff -pruN 1.52.0-1/integration-tests/nghttpx_http1_test.go 1.53.0-1/integration-tests/nghttpx_http1_test.go
--- 1.52.0-1/integration-tests/nghttpx_http1_test.go	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/integration-tests/nghttpx_http1_test.go	2023-05-10 08:27:53.000000000 +0000
@@ -594,6 +594,76 @@ func TestH1H1ReqPhaseReturn(t *testing.T
 	}
 }
 
+// TestH1H1ReqPhaseReturnCONNECTMethod tests that mruby request phase
+// hook resets llhttp HPE_PAUSED_UPGRADE.
+func TestH1H1ReqPhaseReturnCONNECTMethod(t *testing.T) {
+	opts := options{
+		args: []string{"--mruby-file=" + testDir + "/req-return.rb"},
+		handler: func(w http.ResponseWriter, r *http.Request) {
+			t.Fatalf("request should not be forwarded")
+		},
+	}
+	st := newServerTester(t, opts)
+	defer st.Close()
+
+	if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1ReqPhaseReturnCONNECTMethod\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil {
+		t.Fatalf("Error io.WriteString() = %v", err)
+	}
+
+	resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
+	if err != nil {
+		t.Fatalf("Error http.ReadResponse() = %v", err)
+	}
+
+	defer resp.Body.Close()
+
+	if got, want := resp.StatusCode, http.StatusNotFound; got != want {
+		t.Errorf("status: %v; want %v", got, want)
+	}
+
+	hdCheck := func() {
+		hdtests := []struct {
+			k, v string
+		}{
+			{"content-length", "20"},
+			{"from", "mruby"},
+		}
+
+		for _, tt := range hdtests {
+			if got, want := resp.Header.Get(tt.k), tt.v; got != want {
+				t.Errorf("%v = %v; want %v", tt.k, got, want)
+			}
+		}
+
+		if _, err := io.ReadAll(resp.Body); err != nil {
+			t.Fatalf("Error io.ReadAll() = %v", err)
+		}
+	}
+
+	hdCheck()
+
+	if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1ReqPhaseReturnCONNECTMethod\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil {
+		t.Fatalf("Error io.WriteString() = %v", err)
+	}
+
+	resp, err = http.ReadResponse(bufio.NewReader(st.conn), nil)
+	if err != nil {
+		t.Fatalf("Error http.ReadResponse() = %v", err)
+	}
+
+	defer resp.Body.Close()
+
+	if got, want := resp.StatusCode, http.StatusNotFound; got != want {
+		t.Errorf("status: %v; want %v", got, want)
+	}
+
+	hdCheck()
+
+	if _, err := io.ReadAll(resp.Body); err != nil {
+		t.Fatalf("Error io.ReadAll() = %v", err)
+	}
+}
+
 // TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies
 // response header fields.
 func TestH1H1RespPhaseSetHeader(t *testing.T) {
@@ -737,6 +807,54 @@ func TestH1H1POSTRequests(t *testing.T)
 	}
 }
 
+// TestH1H1CONNECTMethodFailure tests that CONNECT method failure
+// resets llhttp HPE_PAUSED_UPGRADE.
+func TestH1H1CONNECTMethodFailure(t *testing.T) {
+	opts := options{
+		handler: func(w http.ResponseWriter, r *http.Request) {
+			if r.Header.Get("required-header") == "" {
+				w.WriteHeader(http.StatusNotFound)
+			}
+		},
+	}
+	st := newServerTester(t, opts)
+	defer st.Close()
+
+	if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1CONNECTMethodFailure\r\nHost: 127.0.0.1:443\r\n\r\n"); err != nil {
+		t.Fatalf("Error io.WriteString() = %v", err)
+	}
+
+	resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
+	if err != nil {
+		t.Fatalf("Error http.ReadResponse() = %v", err)
+	}
+
+	defer resp.Body.Close()
+
+	if got, want := resp.StatusCode, http.StatusNotFound; got != want {
+		t.Errorf("status: %v; want %v", got, want)
+	}
+
+	if _, err := io.ReadAll(resp.Body); err != nil {
+		t.Fatalf("Error io.ReadAll() = %v", err)
+	}
+
+	if _, err := io.WriteString(st.conn, "CONNECT 127.0.0.1:443 HTTP/1.1\r\nTest-Case: TestH1H1CONNECTMethodFailure\r\nHost: 127.0.0.1:443\r\nrequired-header: foo\r\n\r\n"); err != nil {
+		t.Fatalf("Error io.WriteString() = %v", err)
+	}
+
+	resp, err = http.ReadResponse(bufio.NewReader(st.conn), nil)
+	if err != nil {
+		t.Fatalf("Error http.ReadResponse() = %v", err)
+	}
+
+	defer resp.Body.Close()
+
+	if got, want := resp.StatusCode, http.StatusOK; got != want {
+		t.Errorf("status: %v; want %v", got, want)
+	}
+}
+
 // // TestH1H2ConnectFailure tests that server handles the situation that
 // // connection attempt to HTTP/2 backend failed.
 // func TestH1H2ConnectFailure(t *testing.T) {
diff -pruN 1.52.0-1/integration-tests/server_tester.go 1.53.0-1/integration-tests/server_tester.go
--- 1.52.0-1/integration-tests/server_tester.go	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/integration-tests/server_tester.go	2023-05-10 08:27:53.000000000 +0000
@@ -22,7 +22,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/lucas-clemente/quic-go/http3"
+	"github.com/quic-go/quic-go/http3"
 	"github.com/tatsuhiro-t/go-nghttp2"
 	"golang.org/x/net/http2"
 	"golang.org/x/net/http2/hpack"
diff -pruN 1.52.0-1/lib/CMakeLists.txt 1.53.0-1/lib/CMakeLists.txt
--- 1.52.0-1/lib/CMakeLists.txt	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/lib/CMakeLists.txt	2023-05-10 08:27:53.000000000 +0000
@@ -25,6 +25,7 @@ set(NGHTTP2_SOURCES
   nghttp2_rcbuf.c
   nghttp2_extpri.c
   nghttp2_debug.c
+  sfparse.c
 )
 
 set(NGHTTP2_RES "")
diff -pruN 1.52.0-1/lib/Makefile.am 1.53.0-1/lib/Makefile.am
--- 1.52.0-1/lib/Makefile.am	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/lib/Makefile.am	2023-05-10 08:27:53.000000000 +0000
@@ -51,7 +51,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c ngh
 	nghttp2_http.c \
 	nghttp2_rcbuf.c \
 	nghttp2_extpri.c \
-	nghttp2_debug.c
+	nghttp2_debug.c \
+	sfparse.c
 
 HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
 	nghttp2_frame.h \
@@ -68,7 +69,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nght
 	nghttp2_http.h \
 	nghttp2_rcbuf.h \
 	nghttp2_extpri.h \
-	nghttp2_debug.h
+	nghttp2_debug.h \
+	sfparse.h
 
 libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
 libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \
diff -pruN 1.52.0-1/lib/nghttp2_http.c 1.53.0-1/lib/nghttp2_http.c
--- 1.52.0-1/lib/nghttp2_http.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/lib/nghttp2_http.c	2023-05-10 08:27:53.000000000 +0000
@@ -31,6 +31,7 @@
 #include "nghttp2_hd.h"
 #include "nghttp2_helper.h"
 #include "nghttp2_extpri.h"
+#include "sfparse.h"
 
 static uint8_t downcase(uint8_t c) {
   return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
@@ -578,713 +579,52 @@ void nghttp2_http_record_request_method(
   }
 }
 
-/* Generated by genchartbl.py */
-static const int SF_KEY_CHARS[] = {
-    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */, 0 /* EOT  */,
-    0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */, 0 /* BS   */, 0 /* HT   */,
-    0 /* LF   */, 0 /* VT   */, 0 /* FF   */, 0 /* CR   */, 0 /* SO   */,
-    0 /* SI   */, 0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
-    0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */, 0 /* CAN  */,
-    0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */, 0 /* FS   */, 0 /* GS   */,
-    0 /* RS   */, 0 /* US   */, 0 /* SPC  */, 0 /* !    */, 0 /* "    */,
-    0 /* #    */, 0 /* $    */, 0 /* %    */, 0 /* &    */, 0 /* '    */,
-    0 /* (    */, 0 /* )    */, 1 /* *    */, 0 /* +    */, 0 /* ,    */,
-    1 /* -    */, 1 /* .    */, 0 /* /    */, 1 /* 0    */, 1 /* 1    */,
-    1 /* 2    */, 1 /* 3    */, 1 /* 4    */, 1 /* 5    */, 1 /* 6    */,
-    1 /* 7    */, 1 /* 8    */, 1 /* 9    */, 0 /* :    */, 0 /* ;    */,
-    0 /* <    */, 0 /* =    */, 0 /* >    */, 0 /* ?    */, 0 /* @    */,
-    0 /* A    */, 0 /* B    */, 0 /* C    */, 0 /* D    */, 0 /* E    */,
-    0 /* F    */, 0 /* G    */, 0 /* H    */, 0 /* I    */, 0 /* J    */,
-    0 /* K    */, 0 /* L    */, 0 /* M    */, 0 /* N    */, 0 /* O    */,
-    0 /* P    */, 0 /* Q    */, 0 /* R    */, 0 /* S    */, 0 /* T    */,
-    0 /* U    */, 0 /* V    */, 0 /* W    */, 0 /* X    */, 0 /* Y    */,
-    0 /* Z    */, 0 /* [    */, 0 /* \    */, 0 /* ]    */, 0 /* ^    */,
-    1 /* _    */, 0 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
-    1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */, 1 /* h    */,
-    1 /* i    */, 1 /* j    */, 1 /* k    */, 1 /* l    */, 1 /* m    */,
-    1 /* n    */, 1 /* o    */, 1 /* p    */, 1 /* q    */, 1 /* r    */,
-    1 /* s    */, 1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
-    1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */, 0 /* |    */,
-    0 /* }    */, 0 /* ~    */, 0 /* DEL  */, 0 /* 0x80 */, 0 /* 0x81 */,
-    0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
-    0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
-    0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
-    0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
-    0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
-    0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
-    0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
-    0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
-    0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
-    0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
-    0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
-    0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
-    0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
-    0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
-    0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
-    0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
-    0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
-    0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
-    0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
-    0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
-    0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
-    0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
-    0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
-    0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
-    0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
-    0 /* 0xff */,
-};
-
-static ssize_t sf_parse_key(const uint8_t *begin, const uint8_t *end) {
-  const uint8_t *p = begin;
-
-  if ((*p < 'a' || 'z' < *p) && *p != '*') {
-    return -1;
-  }
-
-  for (; p != end && SF_KEY_CHARS[*p]; ++p)
-    ;
-
-  return p - begin;
-}
-
-static ssize_t sf_parse_integer_or_decimal(nghttp2_sf_value *dest,
-                                           const uint8_t *begin,
-                                           const uint8_t *end) {
-  const uint8_t *p = begin;
-  int sign = 1;
-  int64_t value = 0;
-  int type = NGHTTP2_SF_VALUE_TYPE_INTEGER;
-  size_t len = 0;
-  size_t fpos = 0;
-  size_t i;
-
-  if (*p == '-') {
-    if (++p == end) {
-      return -1;
-    }
-
-    sign = -1;
-  }
-
-  if (*p < '0' || '9' < *p) {
-    return -1;
-  }
-
-  for (; p != end; ++p) {
-    switch (*p) {
-    case '0':
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-    case '5':
-    case '6':
-    case '7':
-    case '8':
-    case '9':
-      value *= 10;
-      value += *p - '0';
-
-      if (++len > 15) {
-        return -1;
-      }
-
-      break;
-    case '.':
-      if (type != NGHTTP2_SF_VALUE_TYPE_INTEGER) {
-        goto fin;
-      }
-
-      if (len > 12) {
-        return -1;
-      }
-      fpos = len;
-      type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
-
-      break;
-    default:
-      goto fin;
-    };
-  }
-
-fin:
-  switch (type) {
-  case NGHTTP2_SF_VALUE_TYPE_INTEGER:
-    if (dest) {
-      dest->type = (uint8_t)type;
-      dest->i = value * sign;
-    }
-
-    return p - begin;
-  case NGHTTP2_SF_VALUE_TYPE_DECIMAL:
-    if (fpos == len || len - fpos > 3) {
-      return -1;
-    }
-
-    if (dest) {
-      dest->type = (uint8_t)type;
-      dest->d = (double)value;
-      for (i = len - fpos; i > 0; --i) {
-        dest->d /= (double)10;
-      }
-      dest->d *= sign;
-    }
-
-    return p - begin;
-  default:
-    assert(0);
-    abort();
-  }
-}
-
-/* Generated by genchartbl.py */
-static const int SF_DQUOTE_CHARS[] = {
-    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */, 0 /* EOT  */,
-    0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */, 0 /* BS   */, 0 /* HT   */,
-    0 /* LF   */, 0 /* VT   */, 0 /* FF   */, 0 /* CR   */, 0 /* SO   */,
-    0 /* SI   */, 0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
-    0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */, 0 /* CAN  */,
-    0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */, 0 /* FS   */, 0 /* GS   */,
-    0 /* RS   */, 0 /* US   */, 1 /* SPC  */, 1 /* !    */, 0 /* "    */,
-    1 /* #    */, 1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
-    1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */, 1 /* ,    */,
-    1 /* -    */, 1 /* .    */, 1 /* /    */, 1 /* 0    */, 1 /* 1    */,
-    1 /* 2    */, 1 /* 3    */, 1 /* 4    */, 1 /* 5    */, 1 /* 6    */,
-    1 /* 7    */, 1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
-    1 /* <    */, 1 /* =    */, 1 /* >    */, 1 /* ?    */, 1 /* @    */,
-    1 /* A    */, 1 /* B    */, 1 /* C    */, 1 /* D    */, 1 /* E    */,
-    1 /* F    */, 1 /* G    */, 1 /* H    */, 1 /* I    */, 1 /* J    */,
-    1 /* K    */, 1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
-    1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */, 1 /* T    */,
-    1 /* U    */, 1 /* V    */, 1 /* W    */, 1 /* X    */, 1 /* Y    */,
-    1 /* Z    */, 1 /* [    */, 0 /* \    */, 1 /* ]    */, 1 /* ^    */,
-    1 /* _    */, 1 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
-    1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */, 1 /* h    */,
-    1 /* i    */, 1 /* j    */, 1 /* k    */, 1 /* l    */, 1 /* m    */,
-    1 /* n    */, 1 /* o    */, 1 /* p    */, 1 /* q    */, 1 /* r    */,
-    1 /* s    */, 1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
-    1 /* x    */, 1 /* y    */, 1 /* z    */, 1 /* {    */, 1 /* |    */,
-    1 /* }    */, 1 /* ~    */, 0 /* DEL  */, 0 /* 0x80 */, 0 /* 0x81 */,
-    0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
-    0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
-    0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
-    0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
-    0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
-    0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
-    0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
-    0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
-    0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
-    0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
-    0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
-    0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
-    0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
-    0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
-    0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
-    0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
-    0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
-    0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
-    0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
-    0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
-    0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
-    0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
-    0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
-    0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
-    0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
-    0 /* 0xff */,
-};
-
-static ssize_t sf_parse_string(nghttp2_sf_value *dest, const uint8_t *begin,
-                               const uint8_t *end) {
-  const uint8_t *p = begin;
-
-  if (*p++ != '"') {
-    return -1;
-  }
-
-  for (; p != end; ++p) {
-    switch (*p) {
-    case '\\':
-      if (++p == end) {
-        return -1;
-      }
-
-      switch (*p) {
-      case '"':
-      case '\\':
-        break;
-      default:
-        return -1;
-      }
-
-      break;
-    case '"':
-      if (dest) {
-        dest->type = NGHTTP2_SF_VALUE_TYPE_STRING;
-        dest->s.base = begin + 1;
-        dest->s.len = (size_t)(p - dest->s.base);
-      }
-
-      ++p;
-
-      return p - begin;
-    default:
-      if (!SF_DQUOTE_CHARS[*p]) {
-        return -1;
-      }
-    }
-  }
-
-  return -1;
-}
-
-/* Generated by genchartbl.py */
-static const int SF_TOKEN_CHARS[] = {
-    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */, 0 /* EOT  */,
-    0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */, 0 /* BS   */, 0 /* HT   */,
-    0 /* LF   */, 0 /* VT   */, 0 /* FF   */, 0 /* CR   */, 0 /* SO   */,
-    0 /* SI   */, 0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
-    0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */, 0 /* CAN  */,
-    0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */, 0 /* FS   */, 0 /* GS   */,
-    0 /* RS   */, 0 /* US   */, 0 /* SPC  */, 1 /* !    */, 0 /* "    */,
-    1 /* #    */, 1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
-    0 /* (    */, 0 /* )    */, 1 /* *    */, 1 /* +    */, 0 /* ,    */,
-    1 /* -    */, 1 /* .    */, 1 /* /    */, 1 /* 0    */, 1 /* 1    */,
-    1 /* 2    */, 1 /* 3    */, 1 /* 4    */, 1 /* 5    */, 1 /* 6    */,
-    1 /* 7    */, 1 /* 8    */, 1 /* 9    */, 1 /* :    */, 0 /* ;    */,
-    0 /* <    */, 0 /* =    */, 0 /* >    */, 0 /* ?    */, 0 /* @    */,
-    1 /* A    */, 1 /* B    */, 1 /* C    */, 1 /* D    */, 1 /* E    */,
-    1 /* F    */, 1 /* G    */, 1 /* H    */, 1 /* I    */, 1 /* J    */,
-    1 /* K    */, 1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
-    1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */, 1 /* T    */,
-    1 /* U    */, 1 /* V    */, 1 /* W    */, 1 /* X    */, 1 /* Y    */,
-    1 /* Z    */, 0 /* [    */, 0 /* \    */, 0 /* ]    */, 1 /* ^    */,
-    1 /* _    */, 1 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
-    1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */, 1 /* h    */,
-    1 /* i    */, 1 /* j    */, 1 /* k    */, 1 /* l    */, 1 /* m    */,
-    1 /* n    */, 1 /* o    */, 1 /* p    */, 1 /* q    */, 1 /* r    */,
-    1 /* s    */, 1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
-    1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */, 1 /* |    */,
-    0 /* }    */, 1 /* ~    */, 0 /* DEL  */, 0 /* 0x80 */, 0 /* 0x81 */,
-    0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
-    0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
-    0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
-    0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
-    0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
-    0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
-    0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
-    0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
-    0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
-    0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
-    0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
-    0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
-    0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
-    0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
-    0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
-    0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
-    0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
-    0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
-    0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
-    0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
-    0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
-    0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
-    0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
-    0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
-    0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
-    0 /* 0xff */,
-};
-
-static ssize_t sf_parse_token(nghttp2_sf_value *dest, const uint8_t *begin,
-                              const uint8_t *end) {
-  const uint8_t *p = begin;
-
-  if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') {
-    return -1;
-  }
-
-  for (; p != end && SF_TOKEN_CHARS[*p]; ++p)
-    ;
-
-  if (dest) {
-    dest->type = NGHTTP2_SF_VALUE_TYPE_TOKEN;
-    dest->s.base = begin;
-    dest->s.len = (size_t)(p - begin);
-  }
-
-  return p - begin;
-}
-
-/* Generated by genchartbl.py */
-static const int SF_BYTESEQ_CHARS[] = {
-    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */, 0 /* EOT  */,
-    0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */, 0 /* BS   */, 0 /* HT   */,
-    0 /* LF   */, 0 /* VT   */, 0 /* FF   */, 0 /* CR   */, 0 /* SO   */,
-    0 /* SI   */, 0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
-    0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */, 0 /* CAN  */,
-    0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */, 0 /* FS   */, 0 /* GS   */,
-    0 /* RS   */, 0 /* US   */, 0 /* SPC  */, 0 /* !    */, 0 /* "    */,
-    0 /* #    */, 0 /* $    */, 0 /* %    */, 0 /* &    */, 0 /* '    */,
-    0 /* (    */, 0 /* )    */, 0 /* *    */, 1 /* +    */, 0 /* ,    */,
-    0 /* -    */, 0 /* .    */, 1 /* /    */, 1 /* 0    */, 1 /* 1    */,
-    1 /* 2    */, 1 /* 3    */, 1 /* 4    */, 1 /* 5    */, 1 /* 6    */,
-    1 /* 7    */, 1 /* 8    */, 1 /* 9    */, 0 /* :    */, 0 /* ;    */,
-    0 /* <    */, 1 /* =    */, 0 /* >    */, 0 /* ?    */, 0 /* @    */,
-    1 /* A    */, 1 /* B    */, 1 /* C    */, 1 /* D    */, 1 /* E    */,
-    1 /* F    */, 1 /* G    */, 1 /* H    */, 1 /* I    */, 1 /* J    */,
-    1 /* K    */, 1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
-    1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */, 1 /* T    */,
-    1 /* U    */, 1 /* V    */, 1 /* W    */, 1 /* X    */, 1 /* Y    */,
-    1 /* Z    */, 0 /* [    */, 0 /* \    */, 0 /* ]    */, 0 /* ^    */,
-    0 /* _    */, 0 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
-    1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */, 1 /* h    */,
-    1 /* i    */, 1 /* j    */, 1 /* k    */, 1 /* l    */, 1 /* m    */,
-    1 /* n    */, 1 /* o    */, 1 /* p    */, 1 /* q    */, 1 /* r    */,
-    1 /* s    */, 1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
-    1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */, 0 /* |    */,
-    0 /* }    */, 0 /* ~    */, 0 /* DEL  */, 0 /* 0x80 */, 0 /* 0x81 */,
-    0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
-    0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
-    0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
-    0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
-    0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
-    0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
-    0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
-    0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
-    0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
-    0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
-    0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
-    0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
-    0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
-    0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
-    0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
-    0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
-    0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
-    0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
-    0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
-    0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
-    0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
-    0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
-    0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
-    0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
-    0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
-    0 /* 0xff */,
-};
-
-static ssize_t sf_parse_byteseq(nghttp2_sf_value *dest, const uint8_t *begin,
-                                const uint8_t *end) {
-  const uint8_t *p = begin;
-
-  if (*p++ != ':') {
-    return -1;
-  }
-
-  for (; p != end; ++p) {
-    switch (*p) {
-    case ':':
-      if (dest) {
-        dest->type = NGHTTP2_SF_VALUE_TYPE_BYTESEQ;
-        dest->s.base = begin + 1;
-        dest->s.len = (size_t)(p - dest->s.base);
-      }
-
-      ++p;
-
-      return p - begin;
-    default:
-      if (!SF_BYTESEQ_CHARS[*p]) {
-        return -1;
-      }
-    }
-  }
-
-  return -1;
-}
-
-static ssize_t sf_parse_boolean(nghttp2_sf_value *dest, const uint8_t *begin,
-                                const uint8_t *end) {
-  const uint8_t *p = begin;
-  int b;
-
-  if (*p++ != '?') {
-    return -1;
-  }
-
-  if (p == end) {
-    return -1;
-  }
-
-  switch (*p++) {
-  case '0':
-    b = 0;
-    break;
-  case '1':
-    b = 1;
-    break;
-  default:
-    return -1;
-  }
-
-  if (dest) {
-    dest->type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN;
-    dest->b = b;
-  }
-
-  return p - begin;
-}
-
-static ssize_t sf_parse_bare_item(nghttp2_sf_value *dest, const uint8_t *begin,
-                                  const uint8_t *end) {
-  switch (*begin) {
-  case '-':
-  case '0':
-  case '1':
-  case '2':
-  case '3':
-  case '4':
-  case '5':
-  case '6':
-  case '7':
-  case '8':
-  case '9':
-    return sf_parse_integer_or_decimal(dest, begin, end);
-  case '"':
-    return sf_parse_string(dest, begin, end);
-  case '*':
-    return sf_parse_token(dest, begin, end);
-  case ':':
-    return sf_parse_byteseq(dest, begin, end);
-  case '?':
-    return sf_parse_boolean(dest, begin, end);
-  default:
-    if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) {
-      return sf_parse_token(dest, begin, end);
-    }
-    return -1;
-  }
-}
-
-#define sf_discard_sp_end_err(BEGIN, END, ERR)                                 \
-  for (;; ++(BEGIN)) {                                                         \
-    if ((BEGIN) == (END)) {                                                    \
-      return (ERR);                                                            \
-    }                                                                          \
-    if (*(BEGIN) != ' ') {                                                     \
-      break;                                                                   \
-    }                                                                          \
-  }
-
-static ssize_t sf_parse_params(const uint8_t *begin, const uint8_t *end) {
-  const uint8_t *p = begin;
-  ssize_t slen;
-
-  for (; p != end && *p == ';';) {
-    ++p;
-
-    sf_discard_sp_end_err(p, end, -1);
-
-    slen = sf_parse_key(p, end);
-    if (slen < 0) {
-      return -1;
-    }
-
-    p += slen;
-
-    if (p == end || *p != '=') {
-      /* Boolean true */
-    } else if (++p == end) {
-      return -1;
-    } else {
-      slen = sf_parse_bare_item(NULL, p, end);
-      if (slen < 0) {
-        return -1;
-      }
-
-      p += slen;
-    }
-  }
-
-  return p - begin;
-}
-
-static ssize_t sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
-                             const uint8_t *end) {
-  const uint8_t *p = begin;
-  ssize_t slen;
-
-  slen = sf_parse_bare_item(dest, p, end);
-  if (slen < 0) {
-    return -1;
-  }
-
-  p += slen;
-
-  slen = sf_parse_params(p, end);
-  if (slen < 0) {
-    return -1;
-  }
-
-  p += slen;
-
-  return p - begin;
-}
-
-ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
-                              const uint8_t *end) {
-  return sf_parse_item(dest, begin, end);
-}
-
-static ssize_t sf_parse_inner_list(nghttp2_sf_value *dest, const uint8_t *begin,
-                                   const uint8_t *end) {
-  const uint8_t *p = begin;
-  ssize_t slen;
-
-  if (*p++ != '(') {
-    return -1;
-  }
-
-  for (;;) {
-    sf_discard_sp_end_err(p, end, -1);
-
-    if (*p == ')') {
-      ++p;
-
-      slen = sf_parse_params(p, end);
-      if (slen < 0) {
-        return -1;
-      }
-
-      p += slen;
-
-      if (dest) {
-        dest->type = NGHTTP2_SF_VALUE_TYPE_INNER_LIST;
-      }
-
-      return p - begin;
-    }
-
-    slen = sf_parse_item(NULL, p, end);
-    if (slen < 0) {
-      return -1;
-    }
-
-    p += slen;
-
-    if (p == end || (*p != ' ' && *p != ')')) {
-      return -1;
-    }
-  }
-}
-
-ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest,
-                                    const uint8_t *begin, const uint8_t *end) {
-  return sf_parse_inner_list(dest, begin, end);
-}
-
-static ssize_t sf_parse_item_or_inner_list(nghttp2_sf_value *dest,
-                                           const uint8_t *begin,
-                                           const uint8_t *end) {
-  if (*begin == '(') {
-    return sf_parse_inner_list(dest, begin, end);
-  }
-
-  return sf_parse_item(dest, begin, end);
-}
-
-#define sf_discard_ows(BEGIN, END)                                             \
-  for (;; ++(BEGIN)) {                                                         \
-    if ((BEGIN) == (END)) {                                                    \
-      goto fin;                                                                \
-    }                                                                          \
-    if (*(BEGIN) != ' ' && *(BEGIN) != '\t') {                                 \
-      break;                                                                   \
-    }                                                                          \
-  }
-
-#define sf_discard_ows_end_err(BEGIN, END, ERR)                                \
-  for (;; ++(BEGIN)) {                                                         \
-    if ((BEGIN) == (END)) {                                                    \
-      return (ERR);                                                            \
-    }                                                                          \
-    if (*(BEGIN) != ' ' && *(BEGIN) != '\t') {                                 \
-      break;                                                                   \
-    }                                                                          \
-  }
-
 int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
                                 size_t valuelen) {
-  const uint8_t *p = value, *end = value + valuelen;
-  ssize_t slen;
-  nghttp2_sf_value val;
   nghttp2_extpri pri = *dest;
-  const uint8_t *key;
-  size_t keylen;
+  sf_parser sfp;
+  sf_vec key;
+  sf_value val;
+  int rv;
 
-  for (; p != end && *p == ' '; ++p)
-    ;
+  sf_parser_init(&sfp, value, valuelen);
+
+  for (;;) {
+    rv = sf_parser_dict(&sfp, &key, &val);
+    if (rv != 0) {
+      if (rv == SF_ERR_EOF) {
+        break;
+      }
 
-  for (; p != end;) {
-    slen = sf_parse_key(p, end);
-    if (slen < 0) {
       return NGHTTP2_ERR_INVALID_ARGUMENT;
     }
 
-    key = p;
-    keylen = (size_t)slen;
-
-    p += slen;
-
-    if (p == end || *p != '=') {
-      /* Boolean true */
-      val.type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN;
-      val.b = 1;
+    if (key.len != 1) {
+      continue;
+    }
 
-      slen = sf_parse_params(p, end);
-      if (slen < 0) {
-        return NGHTTP2_ERR_INVALID_ARGUMENT;
-      }
-    } else if (++p == end) {
-      return NGHTTP2_ERR_INVALID_ARGUMENT;
-    } else {
-      slen = sf_parse_item_or_inner_list(&val, p, end);
-      if (slen < 0) {
+    switch (key.base[0]) {
+    case 'i':
+      if (val.type != SF_TYPE_BOOLEAN) {
         return NGHTTP2_ERR_INVALID_ARGUMENT;
       }
-    }
-
-    p += slen;
-
-    if (keylen == 1) {
-      switch (key[0]) {
-      case 'i':
-        if (val.type != NGHTTP2_SF_VALUE_TYPE_BOOLEAN) {
-          return NGHTTP2_ERR_INVALID_ARGUMENT;
-        }
-
-        pri.inc = val.b;
-
-        break;
-      case 'u':
-        if (val.type != NGHTTP2_SF_VALUE_TYPE_INTEGER ||
-            val.i < NGHTTP2_EXTPRI_URGENCY_HIGH ||
-            NGHTTP2_EXTPRI_URGENCY_LOW < val.i) {
-          return NGHTTP2_ERR_INVALID_ARGUMENT;
-        }
 
-        pri.urgency = (uint32_t)val.i;
+      pri.inc = val.boolean;
 
-        break;
+      break;
+    case 'u':
+      if (val.type != SF_TYPE_INTEGER ||
+          val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
+          NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
+        return NGHTTP2_ERR_INVALID_ARGUMENT;
       }
-    }
 
-    sf_discard_ows(p, end);
+      pri.urgency = (uint32_t)val.integer;
 
-    if (*p++ != ',') {
-      return NGHTTP2_ERR_INVALID_ARGUMENT;
+      break;
     }
-
-    sf_discard_ows_end_err(p, end, NGHTTP2_ERR_INVALID_ARGUMENT);
   }
 
-fin:
   *dest = pri;
 
   return 0;
diff -pruN 1.52.0-1/lib/nghttp2_http.h 1.53.0-1/lib/nghttp2_http.h
--- 1.52.0-1/lib/nghttp2_http.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/lib/nghttp2_http.h	2023-05-10 08:27:53.000000000 +0000
@@ -94,54 +94,6 @@ int nghttp2_http_on_data_chunk(nghttp2_s
 void nghttp2_http_record_request_method(nghttp2_stream *stream,
                                         nghttp2_frame *frame);
 
-/*
- * RFC 8941 Structured Field Values.
- */
-typedef enum nghttp2_sf_value_type {
-  NGHTTP2_SF_VALUE_TYPE_BOOLEAN,
-  NGHTTP2_SF_VALUE_TYPE_INTEGER,
-  NGHTTP2_SF_VALUE_TYPE_DECIMAL,
-  NGHTTP2_SF_VALUE_TYPE_STRING,
-  NGHTTP2_SF_VALUE_TYPE_TOKEN,
-  NGHTTP2_SF_VALUE_TYPE_BYTESEQ,
-  NGHTTP2_SF_VALUE_TYPE_INNER_LIST,
-} nghttp2_sf_value_type;
-
-/*
- * nghttp2_sf_value stores Structured Field Values item.  For Inner
- * List, only type is set to NGHTTP2_SF_VALUE_TYPE_INNER_LIST.
- */
-typedef struct nghttp2_sf_value {
-  uint8_t type;
-  union {
-    int b;
-    int64_t i;
-    double d;
-    struct {
-      const uint8_t *base;
-      size_t len;
-    } s;
-  };
-} nghttp2_sf_value;
-
-/*
- * nghttp2_sf_parse_item parses the input sequence [|begin|, |end|)
- * and stores the parsed an Item in |dest|.  It returns the number of
- * bytes consumed if it succeeds, or -1.  This function is declared
- * here for unit tests.
- */
-ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
-                              const uint8_t *end);
-
-/*
- * nghttp2_sf_parse_inner_list parses the input sequence [|begin|, |end|)
- * and stores the parsed an Inner List in |dest|.  It returns the number of
- * bytes consumed if it succeeds, or -1.  This function is declared
- * here for unit tests.
- */
-ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest,
-                                    const uint8_t *begin, const uint8_t *end);
-
 int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
                                 size_t valuelen);
 
diff -pruN 1.52.0-1/lib/nghttp2_map.c 1.53.0-1/lib/nghttp2_map.c
--- 1.52.0-1/lib/nghttp2_map.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/lib/nghttp2_map.c	2023-05-10 08:27:53.000000000 +0000
@@ -31,21 +31,14 @@
 
 #include "nghttp2_helper.h"
 
-#define NGHTTP2_INITIAL_TABLE_LENBITS 8
+#define NGHTTP2_INITIAL_TABLE_LENBITS 4
 
-int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
+void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
   map->mem = mem;
-  map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS;
-  map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS;
-  map->table =
-      nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket));
-  if (map->table == NULL) {
-    return NGHTTP2_ERR_NOMEM;
-  }
-
+  map->tablelen = 0;
+  map->tablelenbits = 0;
+  map->table = NULL;
   map->size = 0;
-
-  return 0;
 }
 
 void nghttp2_map_free(nghttp2_map *map) {
@@ -78,6 +71,10 @@ int nghttp2_map_each(nghttp2_map *map, i
   uint32_t i;
   nghttp2_map_bucket *bkt;
 
+  if (map->size == 0) {
+    return 0;
+  }
+
   for (i = 0; i < map->tablelen; ++i) {
     bkt = &map->table[i];
 
@@ -223,9 +220,17 @@ int nghttp2_map_insert(nghttp2_map *map,
 
   /* Load factor is 0.75 */
   if ((map->size + 1) * 4 > map->tablelen * 3) {
-    rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
-    if (rv != 0) {
-      return rv;
+    if (map->tablelen) {
+      rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
+      if (rv != 0) {
+        return rv;
+      }
+    } else {
+      rv = map_resize(map, 1 << NGHTTP2_INITIAL_TABLE_LENBITS,
+                      NGHTTP2_INITIAL_TABLE_LENBITS);
+      if (rv != 0) {
+        return rv;
+      }
     }
   }
 
@@ -239,11 +244,18 @@ int nghttp2_map_insert(nghttp2_map *map,
 }
 
 void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
-  uint32_t h = hash(key);
-  size_t idx = h2idx(h, map->tablelenbits);
+  uint32_t h;
+  size_t idx;
   nghttp2_map_bucket *bkt;
   size_t d = 0;
 
+  if (map->size == 0) {
+    return NULL;
+  }
+
+  h = hash(key);
+  idx = h2idx(h, map->tablelenbits);
+
   for (;;) {
     bkt = &map->table[idx];
 
@@ -262,11 +274,18 @@ void *nghttp2_map_find(nghttp2_map *map,
 }
 
 int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
-  uint32_t h = hash(key);
-  size_t idx = h2idx(h, map->tablelenbits), didx;
+  uint32_t h;
+  size_t idx, didx;
   nghttp2_map_bucket *bkt;
   size_t d = 0;
 
+  if (map->size == 0) {
+    return NGHTTP2_ERR_INVALID_ARGUMENT;
+  }
+
+  h = hash(key);
+  idx = h2idx(h, map->tablelenbits);
+
   for (;;) {
     bkt = &map->table[idx];
 
@@ -306,6 +325,10 @@ int nghttp2_map_remove(nghttp2_map *map,
 }
 
 void nghttp2_map_clear(nghttp2_map *map) {
+  if (map->tablelen == 0) {
+    return;
+  }
+
   memset(map->table, 0, sizeof(*map->table) * map->tablelen);
   map->size = 0;
 }
diff -pruN 1.52.0-1/lib/nghttp2_map.h 1.53.0-1/lib/nghttp2_map.h
--- 1.52.0-1/lib/nghttp2_map.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/lib/nghttp2_map.h	2023-05-10 08:27:53.000000000 +0000
@@ -54,14 +54,8 @@ typedef struct nghttp2_map {
 
 /*
  * Initializes the map |map|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGHTTP2_ERR_NOMEM
- *   Out of memory
  */
-int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem);
+void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem);
 
 /*
  * Deallocates any resources allocated for |map|. The stored entries
diff -pruN 1.52.0-1/lib/nghttp2_session.c 1.53.0-1/lib/nghttp2_session.c
--- 1.52.0-1/lib/nghttp2_session.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/lib/nghttp2_session.c	2023-05-10 08:27:53.000000000 +0000
@@ -584,10 +584,6 @@ static int session_new(nghttp2_session *
   if (rv != 0) {
     goto fail_hd_inflater;
   }
-  rv = nghttp2_map_init(&(*session_ptr)->streams, mem);
-  if (rv != 0) {
-    goto fail_map;
-  }
 
   nbuffer = ((*session_ptr)->max_send_header_block_length +
              NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /
@@ -605,6 +601,8 @@ static int session_new(nghttp2_session *
     goto fail_aob_framebuf;
   }
 
+  nghttp2_map_init(&(*session_ptr)->streams, mem);
+
   active_outbound_item_reset(&(*session_ptr)->aob, mem);
 
   (*session_ptr)->callbacks = *callbacks;
@@ -637,8 +635,6 @@ static int session_new(nghttp2_session *
   return 0;
 
 fail_aob_framebuf:
-  nghttp2_map_free(&(*session_ptr)->streams);
-fail_map:
   nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
 fail_hd_inflater:
   nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
@@ -5931,7 +5927,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
       in += readlen;
 
       if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
-        return in - first;
+        return (ssize_t)(in - first);
       }
 
       if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
@@ -5968,7 +5964,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
       in += readlen;
 
       if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
-        return in - first;
+        return (ssize_t)(in - first);
       }
 
       nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);
@@ -6468,7 +6464,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
              iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
 
       if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
-        return in - first;
+        return (ssize_t)(in - first);
       }
 
       switch (iframe->frame.hd.type) {
@@ -6772,7 +6768,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
           in += hd_proclen;
           iframe->payloadleft -= hd_proclen;
 
-          return in - first;
+          return (ssize_t)(in - first);
         }
 
         if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
@@ -6963,7 +6959,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
       in += readlen;
 
       if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
-        return in - first;
+        return (ssize_t)(in - first);
       }
 
       nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);
@@ -7021,7 +7017,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
              iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
 
       if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
-        return in - first;
+        return (ssize_t)(in - first);
       }
 
       /* Pad Length field is subject to flow control */
@@ -7171,7 +7167,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
                 session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
                 in - readlen, (size_t)data_readlen, session->user_data);
             if (rv == NGHTTP2_ERR_PAUSE) {
-              return in - first;
+              return (ssize_t)(in - first);
             }
 
             if (nghttp2_is_fatal(rv)) {
@@ -7351,7 +7347,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2
 
   assert(in == last);
 
-  return in - first;
+  return (ssize_t)(in - first);
 }
 
 int nghttp2_session_recv(nghttp2_session *session) {
diff -pruN 1.52.0-1/lib/sfparse.c 1.53.0-1/lib/sfparse.c
--- 1.52.0-1/lib/sfparse.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.53.0-1/lib/sfparse.c	2023-05-10 08:27:53.000000000 +0000
@@ -0,0 +1,1146 @@
+/*
+ * sfparse
+ *
+ * Copyright (c) 2023 sfparse contributors
+ * 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 "sfparse.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#define SF_STATE_DICT 0x08u
+#define SF_STATE_LIST 0x10u
+#define SF_STATE_ITEM 0x18u
+
+#define SF_STATE_INNER_LIST 0x04u
+
+#define SF_STATE_BEFORE 0x00u
+#define SF_STATE_BEFORE_PARAMS 0x01u
+#define SF_STATE_PARAMS 0x02u
+#define SF_STATE_AFTER 0x03u
+
+#define SF_STATE_OP_MASK 0x03u
+
+#define SF_SET_STATE_AFTER(NAME) (SF_STATE_##NAME | SF_STATE_AFTER)
+#define SF_SET_STATE_BEFORE_PARAMS(NAME)                                       \
+  (SF_STATE_##NAME | SF_STATE_BEFORE_PARAMS)
+#define SF_SET_STATE_INNER_LIST_BEFORE(NAME)                                   \
+  (SF_STATE_##NAME | SF_STATE_INNER_LIST | SF_STATE_BEFORE)
+
+#define SF_STATE_DICT_AFTER SF_SET_STATE_AFTER(DICT)
+#define SF_STATE_DICT_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(DICT)
+#define SF_STATE_DICT_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(DICT)
+
+#define SF_STATE_LIST_AFTER SF_SET_STATE_AFTER(LIST)
+#define SF_STATE_LIST_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(LIST)
+#define SF_STATE_LIST_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(LIST)
+
+#define SF_STATE_ITEM_AFTER SF_SET_STATE_AFTER(ITEM)
+#define SF_STATE_ITEM_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(ITEM)
+#define SF_STATE_ITEM_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(ITEM)
+
+#define SF_STATE_INITIAL 0x00u
+
+#define DIGIT_CASES                                                            \
+  case '0':                                                                    \
+  case '1':                                                                    \
+  case '2':                                                                    \
+  case '3':                                                                    \
+  case '4':                                                                    \
+  case '5':                                                                    \
+  case '6':                                                                    \
+  case '7':                                                                    \
+  case '8':                                                                    \
+  case '9'
+
+#define LCALPHA_CASES                                                          \
+  case 'a':                                                                    \
+  case 'b':                                                                    \
+  case 'c':                                                                    \
+  case 'd':                                                                    \
+  case 'e':                                                                    \
+  case 'f':                                                                    \
+  case 'g':                                                                    \
+  case 'h':                                                                    \
+  case 'i':                                                                    \
+  case 'j':                                                                    \
+  case 'k':                                                                    \
+  case 'l':                                                                    \
+  case 'm':                                                                    \
+  case 'n':                                                                    \
+  case 'o':                                                                    \
+  case 'p':                                                                    \
+  case 'q':                                                                    \
+  case 'r':                                                                    \
+  case 's':                                                                    \
+  case 't':                                                                    \
+  case 'u':                                                                    \
+  case 'v':                                                                    \
+  case 'w':                                                                    \
+  case 'x':                                                                    \
+  case 'y':                                                                    \
+  case 'z'
+
+#define UCALPHA_CASES                                                          \
+  case 'A':                                                                    \
+  case 'B':                                                                    \
+  case 'C':                                                                    \
+  case 'D':                                                                    \
+  case 'E':                                                                    \
+  case 'F':                                                                    \
+  case 'G':                                                                    \
+  case 'H':                                                                    \
+  case 'I':                                                                    \
+  case 'J':                                                                    \
+  case 'K':                                                                    \
+  case 'L':                                                                    \
+  case 'M':                                                                    \
+  case 'N':                                                                    \
+  case 'O':                                                                    \
+  case 'P':                                                                    \
+  case 'Q':                                                                    \
+  case 'R':                                                                    \
+  case 'S':                                                                    \
+  case 'T':                                                                    \
+  case 'U':                                                                    \
+  case 'V':                                                                    \
+  case 'W':                                                                    \
+  case 'X':                                                                    \
+  case 'Y':                                                                    \
+  case 'Z'
+
+#define ALPHA_CASES                                                            \
+  UCALPHA_CASES:                                                               \
+  LCALPHA_CASES
+
+#define X20_21_CASES                                                           \
+  case ' ':                                                                    \
+  case '!'
+
+#define X23_5B_CASES                                                           \
+  case '#':                                                                    \
+  case '$':                                                                    \
+  case '%':                                                                    \
+  case '&':                                                                    \
+  case '\'':                                                                   \
+  case '(':                                                                    \
+  case ')':                                                                    \
+  case '*':                                                                    \
+  case '+':                                                                    \
+  case ',':                                                                    \
+  case '-':                                                                    \
+  case '.':                                                                    \
+  case '/':                                                                    \
+  DIGIT_CASES:                                                                 \
+  case ':':                                                                    \
+  case ';':                                                                    \
+  case '<':                                                                    \
+  case '=':                                                                    \
+  case '>':                                                                    \
+  case '?':                                                                    \
+  case '@':                                                                    \
+  UCALPHA_CASES:                                                               \
+  case '['
+
+#define X5D_7E_CASES                                                           \
+  case ']':                                                                    \
+  case '^':                                                                    \
+  case '_':                                                                    \
+  case '`':                                                                    \
+  LCALPHA_CASES:                                                               \
+  case '{':                                                                    \
+  case '|':                                                                    \
+  case '}':                                                                    \
+  case '~'
+
+static int is_ws(uint8_t c) {
+  switch (c) {
+  case ' ':
+  case '\t':
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+static int parser_eof(sf_parser *sfp) { return sfp->pos == sfp->end; }
+
+static void parser_discard_ows(sf_parser *sfp) {
+  for (; !parser_eof(sfp) && is_ws(*sfp->pos); ++sfp->pos)
+    ;
+}
+
+static void parser_discard_sp(sf_parser *sfp) {
+  for (; !parser_eof(sfp) && *sfp->pos == ' '; ++sfp->pos)
+    ;
+}
+
+static void parser_set_op_state(sf_parser *sfp, uint32_t op) {
+  sfp->state &= ~SF_STATE_OP_MASK;
+  sfp->state |= op;
+}
+
+static void parser_unset_inner_list_state(sf_parser *sfp) {
+  sfp->state &= ~SF_STATE_INNER_LIST;
+}
+
+static int parser_key(sf_parser *sfp, sf_vec *dest) {
+  const uint8_t *base;
+
+  switch (*sfp->pos) {
+  case '*':
+  LCALPHA_CASES:
+    break;
+  default:
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  base = sfp->pos++;
+
+  for (; !parser_eof(sfp); ++sfp->pos) {
+    switch (*sfp->pos) {
+    case '_':
+    case '-':
+    case '.':
+    case '*':
+    DIGIT_CASES:
+    LCALPHA_CASES:
+      continue;
+    }
+
+    break;
+  }
+
+  if (dest) {
+    dest->base = (uint8_t *)base;
+    dest->len = (size_t)(sfp->pos - dest->base);
+  }
+
+  return 0;
+}
+
+static int parser_number(sf_parser *sfp, sf_value *dest) {
+  int sign = 1;
+  int64_t value = 0;
+  size_t len = 0;
+  size_t fpos = 0;
+
+  if (*sfp->pos == '-') {
+    ++sfp->pos;
+    if (parser_eof(sfp)) {
+      return SF_ERR_PARSE_ERROR;
+    }
+
+    sign = -1;
+  }
+
+  assert(!parser_eof(sfp));
+
+  for (; !parser_eof(sfp); ++sfp->pos) {
+    switch (*sfp->pos) {
+    DIGIT_CASES:
+      if (++len > 15) {
+        return SF_ERR_PARSE_ERROR;
+      }
+
+      value *= 10;
+      value += *sfp->pos - '0';
+
+      continue;
+    }
+
+    break;
+  }
+
+  if (len == 0) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  if (parser_eof(sfp) || *sfp->pos != '.') {
+    if (dest) {
+      dest->type = SF_TYPE_INTEGER;
+      dest->flags = SF_VALUE_FLAG_NONE;
+      dest->integer = value * sign;
+    }
+
+    return 0;
+  }
+
+  /* decimal */
+
+  if (len > 12) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  fpos = len;
+
+  ++sfp->pos;
+
+  for (; !parser_eof(sfp); ++sfp->pos) {
+    switch (*sfp->pos) {
+    DIGIT_CASES:
+      if (++len > 15) {
+        return SF_ERR_PARSE_ERROR;
+      }
+
+      value *= 10;
+      value += *sfp->pos - '0';
+
+      continue;
+    }
+
+    break;
+  }
+
+  if (fpos == len || len - fpos > 3) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  if (dest) {
+    dest->type = SF_TYPE_DECIMAL;
+    dest->flags = SF_VALUE_FLAG_NONE;
+    dest->decimal.numer = value * sign;
+
+    switch (len - fpos) {
+    case 1:
+      dest->decimal.denom = 10;
+
+      break;
+    case 2:
+      dest->decimal.denom = 100;
+
+      break;
+    case 3:
+      dest->decimal.denom = 1000;
+
+      break;
+    }
+  }
+
+  return 0;
+}
+
+static int parser_date(sf_parser *sfp, sf_value *dest) {
+  int rv;
+  sf_value val;
+
+  /* The first byte has already been validated by the caller. */
+  assert('@' == *sfp->pos);
+
+  ++sfp->pos;
+
+  if (parser_eof(sfp)) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  rv = parser_number(sfp, &val);
+  if (rv != 0) {
+    return rv;
+  }
+
+  if (val.type != SF_TYPE_INTEGER) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  if (dest) {
+    *dest = val;
+    dest->type = SF_TYPE_DATE;
+  }
+
+  return 0;
+}
+
+static int parser_string(sf_parser *sfp, sf_value *dest) {
+  const uint8_t *base;
+  uint32_t flags = SF_VALUE_FLAG_NONE;
+
+  /* The first byte has already been validated by the caller. */
+  assert('"' == *sfp->pos);
+
+  base = ++sfp->pos;
+
+  for (; !parser_eof(sfp); ++sfp->pos) {
+    switch (*sfp->pos) {
+    X20_21_CASES:
+    X23_5B_CASES:
+    X5D_7E_CASES:
+      break;
+    case '\\':
+      ++sfp->pos;
+      if (parser_eof(sfp)) {
+        return SF_ERR_PARSE_ERROR;
+      }
+
+      switch (*sfp->pos) {
+      case '"':
+      case '\\':
+        flags = SF_VALUE_FLAG_ESCAPED_STRING;
+
+        break;
+      default:
+        return SF_ERR_PARSE_ERROR;
+      }
+
+      break;
+    case '"':
+      if (dest) {
+        dest->type = SF_TYPE_STRING;
+        dest->flags = flags;
+        dest->vec.len = (size_t)(sfp->pos - base);
+        dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base;
+      }
+
+      ++sfp->pos;
+
+      return 0;
+    default:
+      return SF_ERR_PARSE_ERROR;
+    }
+  }
+
+  return SF_ERR_PARSE_ERROR;
+}
+
+static int parser_token(sf_parser *sfp, sf_value *dest) {
+  const uint8_t *base;
+
+  /* The first byte has already been validated by the caller. */
+  base = sfp->pos++;
+
+  for (; !parser_eof(sfp); ++sfp->pos) {
+    switch (*sfp->pos) {
+    case '!':
+    case '#':
+    case '$':
+    case '%':
+    case '&':
+    case '\'':
+    case '*':
+    case '+':
+    case '-':
+    case '.':
+    case '^':
+    case '_':
+    case '`':
+    case '|':
+    case '~':
+    case ':':
+    case '/':
+    DIGIT_CASES:
+    ALPHA_CASES:
+      continue;
+    }
+
+    break;
+  }
+
+  if (dest) {
+    dest->type = SF_TYPE_TOKEN;
+    dest->flags = SF_VALUE_FLAG_NONE;
+    dest->vec.base = (uint8_t *)base;
+    dest->vec.len = (size_t)(sfp->pos - base);
+  }
+
+  return 0;
+}
+
+static int parser_byteseq(sf_parser *sfp, sf_value *dest) {
+  const uint8_t *base;
+
+  /* The first byte has already been validated by the caller. */
+  assert(':' == *sfp->pos);
+
+  base = ++sfp->pos;
+
+  for (; !parser_eof(sfp); ++sfp->pos) {
+    switch (*sfp->pos) {
+    case '+':
+    case '/':
+    DIGIT_CASES:
+    ALPHA_CASES:
+      continue;
+    case '=':
+      switch ((sfp->pos - base) & 0x3) {
+      case 0:
+      case 1:
+        return SF_ERR_PARSE_ERROR;
+      case 2:
+        switch (*(sfp->pos - 1)) {
+        case 'A':
+        case 'Q':
+        case 'g':
+        case 'w':
+          break;
+        default:
+          return SF_ERR_PARSE_ERROR;
+        }
+
+        ++sfp->pos;
+
+        if (parser_eof(sfp) || *sfp->pos != '=') {
+          return SF_ERR_PARSE_ERROR;
+        }
+
+        break;
+      case 3:
+        switch (*(sfp->pos - 1)) {
+        case 'A':
+        case 'E':
+        case 'I':
+        case 'M':
+        case 'Q':
+        case 'U':
+        case 'Y':
+        case 'c':
+        case 'g':
+        case 'k':
+        case 'o':
+        case 's':
+        case 'w':
+        case '0':
+        case '4':
+        case '8':
+          break;
+        default:
+          return SF_ERR_PARSE_ERROR;
+        }
+
+        break;
+      }
+
+      ++sfp->pos;
+
+      if (parser_eof(sfp) || *sfp->pos != ':') {
+        return SF_ERR_PARSE_ERROR;
+      }
+
+      goto fin;
+    case ':':
+      if ((sfp->pos - base) & 0x3) {
+        return SF_ERR_PARSE_ERROR;
+      }
+
+      goto fin;
+    default:
+      return SF_ERR_PARSE_ERROR;
+    }
+  }
+
+  return SF_ERR_PARSE_ERROR;
+
+fin:
+  if (dest) {
+    dest->type = SF_TYPE_BYTESEQ;
+    dest->flags = SF_VALUE_FLAG_NONE;
+    dest->vec.len = (size_t)(sfp->pos - base);
+    dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base;
+  }
+
+  ++sfp->pos;
+
+  return 0;
+}
+
+static int parser_boolean(sf_parser *sfp, sf_value *dest) {
+  int b;
+
+  /* The first byte has already been validated by the caller. */
+  assert('?' == *sfp->pos);
+
+  ++sfp->pos;
+
+  if (parser_eof(sfp)) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  switch (*sfp->pos) {
+  case '0':
+    b = 0;
+
+    break;
+  case '1':
+    b = 1;
+
+    break;
+  default:
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  ++sfp->pos;
+
+  if (dest) {
+    dest->type = SF_TYPE_BOOLEAN;
+    dest->flags = SF_VALUE_FLAG_NONE;
+    dest->boolean = b;
+  }
+
+  return 0;
+}
+
+static int parser_bare_item(sf_parser *sfp, sf_value *dest) {
+  switch (*sfp->pos) {
+  case '"':
+    return parser_string(sfp, dest);
+  case '-':
+  DIGIT_CASES:
+    return parser_number(sfp, dest);
+  case '@':
+    return parser_date(sfp, dest);
+  case ':':
+    return parser_byteseq(sfp, dest);
+  case '?':
+    return parser_boolean(sfp, dest);
+  case '*':
+  ALPHA_CASES:
+    return parser_token(sfp, dest);
+  default:
+    return SF_ERR_PARSE_ERROR;
+  }
+}
+
+static int parser_skip_inner_list(sf_parser *sfp);
+
+int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) {
+  int rv;
+
+  switch (sfp->state & SF_STATE_OP_MASK) {
+  case SF_STATE_BEFORE:
+    rv = parser_skip_inner_list(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* fall through */
+  case SF_STATE_BEFORE_PARAMS:
+    parser_set_op_state(sfp, SF_STATE_PARAMS);
+
+    break;
+  case SF_STATE_PARAMS:
+    break;
+  default:
+    assert(0);
+    abort();
+  }
+
+  if (parser_eof(sfp) || *sfp->pos != ';') {
+    parser_set_op_state(sfp, SF_STATE_AFTER);
+
+    return SF_ERR_EOF;
+  }
+
+  ++sfp->pos;
+
+  parser_discard_sp(sfp);
+  if (parser_eof(sfp)) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  rv = parser_key(sfp, dest_key);
+  if (rv != 0) {
+    return rv;
+  }
+
+  if (parser_eof(sfp) || *sfp->pos != '=') {
+    if (dest_value) {
+      dest_value->type = SF_TYPE_BOOLEAN;
+      dest_value->flags = SF_VALUE_FLAG_NONE;
+      dest_value->boolean = 1;
+    }
+
+    return 0;
+  }
+
+  ++sfp->pos;
+
+  if (parser_eof(sfp)) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  return parser_bare_item(sfp, dest_value);
+}
+
+static int parser_skip_params(sf_parser *sfp) {
+  int rv;
+
+  for (;;) {
+    rv = sf_parser_param(sfp, NULL, NULL);
+    switch (rv) {
+    case 0:
+      break;
+    case SF_ERR_EOF:
+      return 0;
+    case SF_ERR_PARSE_ERROR:
+      return rv;
+    default:
+      assert(0);
+      abort();
+    }
+  }
+}
+
+int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) {
+  int rv;
+
+  switch (sfp->state & SF_STATE_OP_MASK) {
+  case SF_STATE_BEFORE:
+    parser_discard_sp(sfp);
+    if (parser_eof(sfp)) {
+      return SF_ERR_PARSE_ERROR;
+    }
+
+    break;
+  case SF_STATE_BEFORE_PARAMS:
+    rv = parser_skip_params(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* Technically, we are entering SF_STATE_AFTER, but we will set
+       another state without reading the state. */
+    /* parser_set_op_state(sfp, SF_STATE_AFTER); */
+
+    /* fall through */
+  case SF_STATE_AFTER:
+    if (parser_eof(sfp)) {
+      return SF_ERR_PARSE_ERROR;
+    }
+
+    switch (*sfp->pos) {
+    case ' ':
+      parser_discard_sp(sfp);
+      if (parser_eof(sfp)) {
+        return SF_ERR_PARSE_ERROR;
+      }
+
+      break;
+    case ')':
+      break;
+    default:
+      return SF_ERR_PARSE_ERROR;
+    }
+
+    break;
+  default:
+    assert(0);
+    abort();
+  }
+
+  if (*sfp->pos == ')') {
+    ++sfp->pos;
+
+    parser_unset_inner_list_state(sfp);
+    parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS);
+
+    return SF_ERR_EOF;
+  }
+
+  rv = parser_bare_item(sfp, dest);
+  if (rv != 0) {
+    return rv;
+  }
+
+  parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS);
+
+  return 0;
+}
+
+static int parser_skip_inner_list(sf_parser *sfp) {
+  int rv;
+
+  for (;;) {
+    rv = sf_parser_inner_list(sfp, NULL);
+    switch (rv) {
+    case 0:
+      break;
+    case SF_ERR_EOF:
+      return 0;
+    case SF_ERR_PARSE_ERROR:
+      return rv;
+    default:
+      assert(0);
+      abort();
+    }
+  }
+}
+
+static int parser_next_key_or_item(sf_parser *sfp) {
+  parser_discard_ows(sfp);
+
+  if (parser_eof(sfp)) {
+    return SF_ERR_EOF;
+  }
+
+  if (*sfp->pos != ',') {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  ++sfp->pos;
+
+  parser_discard_ows(sfp);
+  if (parser_eof(sfp)) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  return 0;
+}
+
+static int parser_dict_value(sf_parser *sfp, sf_value *dest) {
+  int rv;
+
+  if (parser_eof(sfp) || *(sfp->pos) != '=') {
+    /* Boolean true */
+    if (dest) {
+      dest->type = SF_TYPE_BOOLEAN;
+      dest->flags = SF_VALUE_FLAG_NONE;
+      dest->boolean = 1;
+    }
+
+    sfp->state = SF_STATE_DICT_BEFORE_PARAMS;
+
+    return 0;
+  }
+
+  ++sfp->pos;
+
+  if (parser_eof(sfp)) {
+    return SF_ERR_PARSE_ERROR;
+  }
+
+  if (*sfp->pos == '(') {
+    if (dest) {
+      dest->type = SF_TYPE_INNER_LIST;
+      dest->flags = SF_VALUE_FLAG_NONE;
+    }
+
+    ++sfp->pos;
+
+    sfp->state = SF_STATE_DICT_INNER_LIST_BEFORE;
+
+    return 0;
+  }
+
+  rv = parser_bare_item(sfp, dest);
+  if (rv != 0) {
+    return rv;
+  }
+
+  sfp->state = SF_STATE_DICT_BEFORE_PARAMS;
+
+  return 0;
+}
+
+int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) {
+  int rv;
+
+  switch (sfp->state) {
+  case SF_STATE_DICT_INNER_LIST_BEFORE:
+    rv = parser_skip_inner_list(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* fall through */
+  case SF_STATE_DICT_BEFORE_PARAMS:
+    rv = parser_skip_params(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* fall through */
+  case SF_STATE_DICT_AFTER:
+    rv = parser_next_key_or_item(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    break;
+  case SF_STATE_INITIAL:
+    parser_discard_sp(sfp);
+
+    if (parser_eof(sfp)) {
+      return SF_ERR_EOF;
+    }
+
+    break;
+  default:
+    assert(0);
+    abort();
+  }
+
+  rv = parser_key(sfp, dest_key);
+  if (rv != 0) {
+    return rv;
+  }
+
+  return parser_dict_value(sfp, dest_value);
+}
+
+int sf_parser_list(sf_parser *sfp, sf_value *dest) {
+  int rv;
+
+  switch (sfp->state) {
+  case SF_STATE_LIST_INNER_LIST_BEFORE:
+    rv = parser_skip_inner_list(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* fall through */
+  case SF_STATE_LIST_BEFORE_PARAMS:
+    rv = parser_skip_params(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* fall through */
+  case SF_STATE_LIST_AFTER:
+    rv = parser_next_key_or_item(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    break;
+  case SF_STATE_INITIAL:
+    parser_discard_sp(sfp);
+
+    if (parser_eof(sfp)) {
+      return SF_ERR_EOF;
+    }
+
+    break;
+  default:
+    assert(0);
+    abort();
+  }
+
+  if (*sfp->pos == '(') {
+    if (dest) {
+      dest->type = SF_TYPE_INNER_LIST;
+      dest->flags = SF_VALUE_FLAG_NONE;
+    }
+
+    ++sfp->pos;
+
+    sfp->state = SF_STATE_LIST_INNER_LIST_BEFORE;
+
+    return 0;
+  }
+
+  rv = parser_bare_item(sfp, dest);
+  if (rv != 0) {
+    return rv;
+  }
+
+  sfp->state = SF_STATE_LIST_BEFORE_PARAMS;
+
+  return 0;
+}
+
+int sf_parser_item(sf_parser *sfp, sf_value *dest) {
+  int rv;
+
+  switch (sfp->state) {
+  case SF_STATE_INITIAL:
+    parser_discard_sp(sfp);
+
+    if (parser_eof(sfp)) {
+      return SF_ERR_PARSE_ERROR;
+    }
+
+    break;
+  case SF_STATE_ITEM_INNER_LIST_BEFORE:
+    rv = parser_skip_inner_list(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* fall through */
+  case SF_STATE_ITEM_BEFORE_PARAMS:
+    rv = parser_skip_params(sfp);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* fall through */
+  case SF_STATE_ITEM_AFTER:
+    parser_discard_sp(sfp);
+
+    if (!parser_eof(sfp)) {
+      return SF_ERR_PARSE_ERROR;
+    }
+
+    return SF_ERR_EOF;
+  default:
+    assert(0);
+    abort();
+  }
+
+  if (*sfp->pos == '(') {
+    if (dest) {
+      dest->type = SF_TYPE_INNER_LIST;
+      dest->flags = SF_VALUE_FLAG_NONE;
+    }
+
+    ++sfp->pos;
+
+    sfp->state = SF_STATE_ITEM_INNER_LIST_BEFORE;
+
+    return 0;
+  }
+
+  rv = parser_bare_item(sfp, dest);
+  if (rv != 0) {
+    return rv;
+  }
+
+  sfp->state = SF_STATE_ITEM_BEFORE_PARAMS;
+
+  return 0;
+}
+
+void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen) {
+  if (datalen == 0) {
+    sfp->pos = sfp->end = NULL;
+  } else {
+    sfp->pos = data;
+    sfp->end = data + datalen;
+  }
+
+  sfp->state = SF_STATE_INITIAL;
+}
+
+void sf_unescape(sf_vec *dest, const sf_vec *src) {
+  const uint8_t *p, *q;
+  uint8_t *o;
+  size_t len, slen;
+
+  if (src->len == 0) {
+    *dest = *src;
+
+    return;
+  }
+
+  o = dest->base;
+  p = src->base;
+  len = src->len;
+
+  for (;;) {
+    q = memchr(p, '\\', len);
+    if (q == NULL) {
+      if (len == src->len) {
+        *dest = *src;
+
+        return;
+      }
+
+      memcpy(o, p, len);
+      o += len;
+
+      break;
+    }
+
+    slen = (size_t)(q - p);
+    memcpy(o, p, slen);
+    o += slen;
+
+    p = q + 1;
+    *o++ = *p++;
+    len -= slen + 2;
+  }
+
+  dest->len = (size_t)(o - dest->base);
+}
+
+void sf_base64decode(sf_vec *dest, const sf_vec *src) {
+  static const int index_tbl[] = {
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
+      58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,
+      7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+      25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+      37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1};
+  uint8_t *o;
+  const uint8_t *p, *end;
+  uint32_t n;
+  size_t i;
+  int idx;
+
+  assert((src->len & 0x3) == 0);
+
+  if (src->len == 0) {
+    *dest = *src;
+
+    return;
+  }
+
+  o = dest->base;
+  p = src->base;
+  end = src->base + src->len;
+
+  for (; p != end;) {
+    n = 0;
+
+    for (i = 1; i <= 4; ++i, ++p) {
+      idx = index_tbl[*p];
+
+      if (idx == -1) {
+        assert(i > 2);
+
+        if (i == 3) {
+          assert(*p == '=' && *(p + 1) == '=' && p + 2 == end);
+
+          *o++ = (uint8_t)(n >> 16);
+
+          goto fin;
+        }
+
+        assert(*p == '=' && p + 1 == end);
+
+        *o++ = (uint8_t)(n >> 16);
+        *o++ = (n >> 8) & 0xffu;
+
+        goto fin;
+      }
+
+      n += (uint32_t)(idx << (24 - i * 6));
+    }
+
+    *o++ = (uint8_t)(n >> 16);
+    *o++ = (n >> 8) & 0xffu;
+    *o++ = n & 0xffu;
+  }
+
+fin:
+  dest->len = (size_t)(o - dest->base);
+}
diff -pruN 1.52.0-1/lib/sfparse.h 1.53.0-1/lib/sfparse.h
--- 1.52.0-1/lib/sfparse.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.53.0-1/lib/sfparse.h	2023-05-10 08:27:53.000000000 +0000
@@ -0,0 +1,409 @@
+/*
+ * sfparse
+ *
+ * Copyright (c) 2023 sfparse contributors
+ * 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 SFPARSE_H
+#define SFPARSE_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
+
+#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 <stddef.h>
+
+/**
+ * @enum
+ *
+ * :type:`sf_type` defines value type.
+ */
+typedef enum sf_type {
+  /**
+   * :enum:`SF_TYPE_BOOLEAN` indicates boolean type.
+   */
+  SF_TYPE_BOOLEAN,
+  /**
+   * :enum:`SF_TYPE_INTEGER` indicates integer type.
+   */
+  SF_TYPE_INTEGER,
+  /**
+   * :enum:`SF_TYPE_DECIMAL` indicates decimal type.
+   */
+  SF_TYPE_DECIMAL,
+  /**
+   * :enum:`SF_TYPE_STRING` indicates string type.
+   */
+  SF_TYPE_STRING,
+  /**
+   * :enum:`SF_TYPE_TOKEN` indicates token type.
+   */
+  SF_TYPE_TOKEN,
+  /**
+   * :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type.
+   */
+  SF_TYPE_BYTESEQ,
+  /**
+   * :enum:`SF_TYPE_INNER_LIST` indicates inner list type.
+   */
+  SF_TYPE_INNER_LIST,
+  /**
+   * :enum:`SF_TYPE_DATE` indicates date type.
+   */
+  SF_TYPE_DATE
+} sf_type;
+
+/**
+ * @macro
+ *
+ * :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has
+ * occurred, and it is not possible to continue the processing.
+ */
+#define SF_ERR_PARSE_ERROR -1
+
+/**
+ * @macro
+ *
+ * :macro:`SF_ERR_EOF` indicates that there is nothing left to read.
+ * The context of this error varies depending on the function that
+ * returns this error code.
+ */
+#define SF_ERR_EOF -2
+
+/**
+ * @struct
+ *
+ * :type:`sf_vec` stores sequence of bytes.
+ */
+typedef struct sf_vec {
+  /**
+   * :member:`base` points to the beginning of the sequence of bytes.
+   */
+  uint8_t *base;
+  /**
+   * :member:`len` is the number of bytes contained in this sequence.
+   */
+  size_t len;
+} sf_vec;
+
+/**
+ * @macro
+ *
+ * :macro:`SF_VALUE_FLAG_NONE` indicates no flag set.
+ */
+#define SF_VALUE_FLAG_NONE 0x0u
+
+/**
+ * @macro
+ *
+ * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string
+ * contains escaped character(s).
+ */
+#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u
+
+/**
+ * @struct
+ *
+ * :type:`sf_decimal` contains decimal value.
+ */
+typedef struct sf_decimal {
+  /**
+   * :member:`numer` contains numerator of the decimal value.
+   */
+  int64_t numer;
+  /**
+   * :member:`denom` contains denominator of the decimal value.
+   */
+  int64_t denom;
+} sf_decimal;
+
+/**
+ * @struct
+ *
+ * :type:`sf_value` stores a Structured Field item.  For Inner List,
+ * only type is set to :enum:`sf_type.SF_TYPE_INNER_LIST`.  In order
+ * to read the items contained in an inner list, call
+ * `sf_parser_inner_list`.
+ */
+typedef struct sf_value {
+  /**
+   * :member:`type` is the type of the value contained in this
+   * particular object.
+   */
+  sf_type type;
+  /**
+   * :member:`flags` is bitwise OR of one or more of
+   * :macro:`SF_VALUE_FLAG_* <SF_VALUE_FLAG_NONE>`.
+   */
+  uint32_t flags;
+  /**
+   * @anonunion_start
+   *
+   * @sf_value_value
+   */
+  union {
+    /**
+     * :member:`boolean` contains boolean value if :member:`type` ==
+     * :enum:`sf_type.SF_TYPE_BOOLEAN`.  1 indicates true, and 0
+     * indicates false.
+     */
+    int boolean;
+    /**
+     * :member:`integer` contains integer value if :member:`type` is
+     * either :enum:`sf_type.SF_TYPE_INTEGER` or
+     * :enum:`sf_type.SF_TYPE_DATE`.
+     */
+    int64_t integer;
+    /**
+     * :member:`decimal` contains decimal value if :member:`type` ==
+     * :enum:`sf_type.SF_TYPE_DECIMAL`.
+     */
+    sf_decimal decimal;
+    /**
+     * :member:`vec` contains sequence of bytes if :member:`type` is
+     * either :enum:`sf_type.SF_TYPE_STRING`,
+     * :enum:`sf_type.SF_TYPE_TOKEN`, or
+     * :enum:`sf_type.SF_TYPE_BYTESEQ`.
+     *
+     * For :enum:`sf_type.SF_TYPE_STRING`, this field contains one or
+     * more escaped characters if :member:`flags` has
+     * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` set.  To unescape the
+     * string, use `sf_unescape`.
+     *
+     * For :enum:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64
+     * encoded string.  To decode this byte string, use
+     * `sf_base64decode`.
+     *
+     * If :member:`vec.len <sf_vec.len>` == 0, :member:`vec.base
+     * <sf_vec.base>` is guaranteed to be NULL.
+     */
+    sf_vec vec;
+    /**
+     * @anonunion_end
+     */
+  };
+} sf_value;
+
+/**
+ * @struct
+ *
+ * :type:`sf_parser` is the Structured Field Values parser.  Use
+ * `sf_parser_init` to initialize it.
+ */
+typedef struct sf_parser {
+  /* all fields are private */
+  const uint8_t *pos;
+  const uint8_t *end;
+  uint32_t state;
+} sf_parser;
+
+/**
+ * @function
+ *
+ * `sf_parser_init` initializes |sfp| with the given buffer pointed by
+ * |data| of length |datalen|.
+ */
+void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen);
+
+/**
+ * @function
+ *
+ * `sf_parser_param` reads a parameter.  If this function returns 0,
+ * it stores parameter key and value in |dest_key| and |dest_value|
+ * respectively, if they are not NULL.
+ *
+ * This function does no effort to find duplicated keys.  Same key may
+ * be reported more than once.
+ *
+ * Caller should keep calling this function until it returns negative
+ * error code.  If it returns :macro:`SF_ERR_EOF`, all parameters have
+ * read, and caller can continue to read rest of the values.  If it
+ * returns :macro:`SF_ERR_PARSE_ERROR`, it encountered fatal error
+ * while parsing field value.
+ */
+int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value);
+
+/**
+ * @function
+ *
+ * `sf_parser_dict` reads the next dictionary key and value pair.  If
+ * this function returns 0, it stores the key and value in |dest_key|
+ * and |dest_value| respectively, if they are not NULL.
+ *
+ * Caller can optionally read parameters attached to the pair by
+ * calling `sf_parser_param`.
+ *
+ * This function does no effort to find duplicated keys.  Same key may
+ * be reported more than once.
+ *
+ * Caller should keep calling this function until it returns negative
+ * error code.  If it returns :macro:`SF_ERR_EOF`, all key and value
+ * pairs have been read, and there is nothing left to read.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`SF_ERR_EOF`
+ *     All values in the dictionary have read.
+ * :macro:`SF_ERR_PARSE_ERROR`
+ *     It encountered fatal error while parsing field value.
+ */
+int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value);
+
+/**
+ * @function
+ *
+ * `sf_parser_list` reads the next list item.  If this function
+ * returns 0, it stores the item in |dest| if it is not NULL.
+ *
+ * Caller can optionally read parameters attached to the item by
+ * calling `sf_parser_param`.
+ *
+ * Caller should keep calling this function until it returns negative
+ * error code.  If it returns :macro:`SF_ERR_EOF`, all values in the
+ * list have been read, and there is nothing left to read.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`SF_ERR_EOF`
+ *     All values in the list have read.
+ * :macro:`SF_ERR_PARSE_ERROR`
+ *     It encountered fatal error while parsing field value.
+ */
+int sf_parser_list(sf_parser *sfp, sf_value *dest);
+
+/**
+ * @function
+ *
+ * `sf_parser_item` reads a single item.  If this function returns 0,
+ * it stores the item in |dest| if it is not NULL.
+ *
+ * This function is only used for the field value that consists of a
+ * single item.
+ *
+ * Caller can optionally read parameters attached to the item by
+ * calling `sf_parser_param`.
+ *
+ * Caller should call this function again to make sure that there is
+ * nothing left to read.  If this 2nd function call returns
+ * :macro:`SF_ERR_EOF`, all data have been processed successfully.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`SF_ERR_EOF`
+ *     There is nothing left to read.
+ * :macro:`SF_ERR_PARSE_ERROR`
+ *     It encountered fatal error while parsing field value.
+ */
+int sf_parser_item(sf_parser *sfp, sf_value *dest);
+
+/**
+ * @function
+ *
+ * `sf_parser_inner_list` reads the next inner list item.  If this
+ * function returns 0, it stores the item in |dest| if it is not NULL.
+ *
+ * Caller can optionally read parameters attached to the item by
+ * calling `sf_parser_param`.
+ *
+ * Caller should keep calling this function until it returns negative
+ * error code.  If it returns :macro:`SF_ERR_EOF`, all values in this
+ * inner list have been read, and caller can optionally read
+ * parameters attached to this inner list by calling
+ * `sf_parser_param`.  Then caller can continue to read rest of the
+ * values.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`SF_ERR_EOF`
+ *     All values in the inner list have read.
+ * :macro:`SF_ERR_PARSE_ERROR`
+ *     It encountered fatal error while parsing field value.
+ */
+int sf_parser_inner_list(sf_parser *sfp, sf_value *dest);
+
+/**
+ * @function
+ *
+ * `sf_unescape` copies |src| to |dest| by removing escapes (``\``).
+ * |src| should be the pointer to :member:`sf_value.vec` of type
+ * :enum:`sf_type.SF_TYPE_STRING` produced by either `sf_parser_dict`,
+ * `sf_parser_list`, `sf_parser_inner_list`, `sf_parser_item`, or
+ * `sf_parser_param`, otherwise the behavior is undefined.
+ *
+ * :member:`dest->base <sf_vec.base>` must point to the buffer that
+ * has sufficient space to store the unescaped string.
+ *
+ * If there is no escape character in |src|, |*src| is assigned to
+ * |*dest|.  This includes the case that :member:`src->len
+ * <sf_vec.len>` == 0.
+ *
+ * This function sets the length of unescaped string to
+ * :member:`dest->len <sf_vec.len>`.
+ */
+void sf_unescape(sf_vec *dest, const sf_vec *src);
+
+/**
+ * @function
+ *
+ * `sf_base64decode` decodes Base64 encoded string |src| and writes
+ * the result into |dest|.  |src| should be the pointer to
+ * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ`
+ * produced by either `sf_parser_dict`, `sf_parser_list`,
+ * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`,
+ * otherwise the behavior is undefined.
+ *
+ * :member:`dest->base <sf_vec.base>` must point to the buffer that
+ * has sufficient space to store the decoded byte string.
+ *
+ * If :member:`src->len <sf_vec.len>` == 0, |*src| is assigned to
+ * |*dest|.
+ *
+ * This function sets the length of decoded byte string to
+ * :member:`dest->len <sf_vec.len>`.
+ */
+void sf_base64decode(sf_vec *dest, const sf_vec *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SFPARSE_H */
diff -pruN 1.52.0-1/makerelease.sh 1.53.0-1/makerelease.sh
--- 1.52.0-1/makerelease.sh	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/makerelease.sh	2023-05-10 08:27:53.000000000 +0000
@@ -11,4 +11,10 @@ git submodule update --init
 autoreconf -i
 ./configure --with-mruby && \
     make dist-bzip2 && make dist-gzip && make dist-xz || echo "error"
+
+VERSION=`echo -n $TAG | sed -E 's|^v([0-9]+\.[0-9]+\.[0-9]+)(-DEV)?$|\1|'`
+for f in nghttp2-$VERSION.tar.bz2 nghttp2-$VERSION.tar.gz nghttp2-$VERSION.tar.xz; do
+    sha256sum $f > $f.asc
+done
+
 make distclean
diff -pruN 1.52.0-1/src/Makefile.am 1.53.0-1/src/Makefile.am
--- 1.52.0-1/src/Makefile.am	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/Makefile.am	2023-05-10 08:27:53.000000000 +0000
@@ -20,6 +20,8 @@
 # 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.
+SUBDIRS = testdata
+
 EXTRA_DIST = \
 	CMakeLists.txt \
 	test.example.com.pem \
diff -pruN 1.52.0-1/src/app_helper.cc 1.53.0-1/src/app_helper.cc
--- 1.52.0-1/src/app_helper.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/app_helper.cc	2023-05-10 08:27:53.000000000 +0000
@@ -480,15 +480,10 @@ std::chrono::steady_clock::time_point ge
 ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
                      size_t inlen) {
   int rv;
-  z_stream zst;
+  z_stream zst{};
   uint8_t temp_out[8_k];
   auto temp_outlen = sizeof(temp_out);
 
-  zst.next_in = Z_NULL;
-  zst.zalloc = Z_NULL;
-  zst.zfree = Z_NULL;
-  zst.opaque = Z_NULL;
-
   rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9,
                     Z_DEFAULT_STRATEGY);
 
diff -pruN 1.52.0-1/src/h2load.cc 1.53.0-1/src/h2load.cc
--- 1.52.0-1/src/h2load.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/h2load.cc	2023-05-10 08:27:53.000000000 +0000
@@ -506,7 +506,7 @@ Client::Client(uint32_t id, Worker *work
     quic.tx.data = std::make_unique<uint8_t[]>(64_k);
   }
 
-  ngtcp2_connection_close_error_default(&quic.last_error);
+  ngtcp2_ccerr_default(&quic.last_error);
 #endif // ENABLE_HTTP3
 }
 
diff -pruN 1.52.0-1/src/h2load.h 1.53.0-1/src/h2load.h
--- 1.52.0-1/src/h2load.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/h2load.h	2023-05-10 08:27:53.000000000 +0000
@@ -342,7 +342,7 @@ struct Client {
     ngtcp2_crypto_conn_ref conn_ref;
     ev_timer pkt_timer;
     ngtcp2_conn *conn;
-    ngtcp2_connection_close_error last_error;
+    ngtcp2_ccerr last_error;
     bool close_requested;
     FILE *qlog_file;
 
diff -pruN 1.52.0-1/src/h2load_http3_session.cc 1.53.0-1/src/h2load_http3_session.cc
--- 1.52.0-1/src/h2load_http3_session.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/h2load_http3_session.cc	2023-05-10 08:27:53.000000000 +0000
@@ -305,7 +305,7 @@ int Http3Session::init_conn() {
 
   assert(conn_ == nullptr);
 
-  if (ngtcp2_conn_get_max_local_streams_uni(client_->quic.conn) < 3) {
+  if (ngtcp2_conn_get_streams_uni_left(client_->quic.conn) < 3) {
     return -1;
   }
 
@@ -395,7 +395,7 @@ ssize_t Http3Session::read_stream(uint32
   if (nconsumed < 0) {
     std::cerr << "nghttp3_conn_read_stream: " << nghttp3_strerror(nconsumed)
               << std::endl;
-    ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_ccerr_set_application_error(
         &client_->quic.last_error,
         nghttp3_err_infer_quic_app_error_code(nconsumed), nullptr, 0);
     return -1;
@@ -408,7 +408,7 @@ ssize_t Http3Session::write_stream(int64
   auto sveccnt =
       nghttp3_conn_writev_stream(conn_, &stream_id, &fin, vec, veccnt);
   if (sveccnt < 0) {
-    ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_ccerr_set_application_error(
         &client_->quic.last_error,
         nghttp3_err_infer_quic_app_error_code(sveccnt), nullptr, 0);
     return -1;
@@ -427,7 +427,7 @@ void Http3Session::shutdown_stream_write
 int Http3Session::add_write_offset(int64_t stream_id, size_t ndatalen) {
   auto rv = nghttp3_conn_add_write_offset(conn_, stream_id, ndatalen);
   if (rv != 0) {
-    ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_ccerr_set_application_error(
         &client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(rv),
         nullptr, 0);
     return -1;
@@ -438,7 +438,7 @@ int Http3Session::add_write_offset(int64
 int Http3Session::add_ack_offset(int64_t stream_id, size_t datalen) {
   auto rv = nghttp3_conn_add_ack_offset(conn_, stream_id, datalen);
   if (rv != 0) {
-    ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_ccerr_set_application_error(
         &client_->quic.last_error, nghttp3_err_infer_quic_app_error_code(rv),
         nullptr, 0);
     return -1;
diff -pruN 1.52.0-1/src/h2load_quic.cc 1.53.0-1/src/h2load_quic.cc
--- 1.52.0-1/src/h2load_quic.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/h2load_quic.cc	2023-05-10 08:27:53.000000000 +0000
@@ -496,8 +496,7 @@ int Client::quic_pkt_timeout() {
 
   rv = ngtcp2_conn_handle_expiry(quic.conn, now);
   if (rv != 0) {
-    ngtcp2_connection_close_error_set_transport_error_liberr(&quic.last_error,
-                                                             rv, nullptr, 0);
+    ngtcp2_ccerr_set_liberr(&quic.last_error, rv, nullptr, 0);
     return -1;
   }
 
@@ -550,12 +549,11 @@ int Client::read_quic() {
 
       if (!quic.last_error.error_code) {
         if (rv == NGTCP2_ERR_CRYPTO) {
-          ngtcp2_connection_close_error_set_transport_error_tls_alert(
-              &quic.last_error, ngtcp2_conn_get_tls_alert(quic.conn), nullptr,
-              0);
+          ngtcp2_ccerr_set_tls_alert(&quic.last_error,
+                                     ngtcp2_conn_get_tls_alert(quic.conn),
+                                     nullptr, 0);
         } else {
-          ngtcp2_connection_close_error_set_transport_error_liberr(
-              &quic.last_error, rv, nullptr, 0);
+          ngtcp2_ccerr_set_liberr(&quic.last_error, rv, nullptr, 0);
         }
       }
 
@@ -651,8 +649,7 @@ int Client::write_quic() {
         continue;
       }
 
-      ngtcp2_connection_close_error_set_transport_error_liberr(
-          &quic.last_error, nwrite, nullptr, 0);
+      ngtcp2_ccerr_set_liberr(&quic.last_error, nwrite, nullptr, 0);
       return -1;
     } else if (ndatalen >= 0 && s->add_write_offset(stream_id, ndatalen) != 0) {
       return -1;
diff -pruN 1.52.0-1/src/nghttp2_gzip.c 1.53.0-1/src/nghttp2_gzip.c
--- 1.52.0-1/src/nghttp2_gzip.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/nghttp2_gzip.c	2023-05-10 08:27:53.000000000 +0000
@@ -28,16 +28,10 @@
 
 int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr) {
   int rv;
-  *inflater_ptr = malloc(sizeof(nghttp2_gzip));
+  *inflater_ptr = calloc(1, sizeof(nghttp2_gzip));
   if (*inflater_ptr == NULL) {
     return -1;
   }
-  (*inflater_ptr)->finished = 0;
-  (*inflater_ptr)->zst.next_in = Z_NULL;
-  (*inflater_ptr)->zst.avail_in = 0;
-  (*inflater_ptr)->zst.zalloc = Z_NULL;
-  (*inflater_ptr)->zst.zfree = Z_NULL;
-  (*inflater_ptr)->zst.opaque = Z_NULL;
   rv = inflateInit2(&(*inflater_ptr)->zst, 47);
   if (rv != Z_OK) {
     free(*inflater_ptr);
diff -pruN 1.52.0-1/src/nghttp2_gzip_test.c 1.53.0-1/src/nghttp2_gzip_test.c
--- 1.52.0-1/src/nghttp2_gzip_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/nghttp2_gzip_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -36,11 +36,7 @@
 static size_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
                            size_t inlen) {
   int rv;
-  z_stream zst;
-  zst.next_in = Z_NULL;
-  zst.zalloc = Z_NULL;
-  zst.zfree = Z_NULL;
-  zst.opaque = Z_NULL;
+  z_stream zst = {0};
 
   rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION);
   CU_ASSERT(rv == Z_OK);
diff -pruN 1.52.0-1/src/shrpx-unittest.cc 1.53.0-1/src/shrpx-unittest.cc
--- 1.52.0-1/src/shrpx-unittest.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx-unittest.cc	2023-05-10 08:27:53.000000000 +0000
@@ -77,6 +77,10 @@ int main(int argc, char *argv[]) {
                    shrpx::test_shrpx_tls_cert_lookup_tree_add_ssl_ctx) ||
       !CU_add_test(pSuite, "tls_tls_hostname_match",
                    shrpx::test_shrpx_tls_tls_hostname_match) ||
+      !CU_add_test(pSuite, "tls_tls_verify_numeric_hostname",
+                   shrpx::test_shrpx_tls_verify_numeric_hostname) ||
+      !CU_add_test(pSuite, "tls_tls_verify_dns_hostname",
+                   shrpx::test_shrpx_tls_verify_dns_hostname) ||
       !CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
       !CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) ||
       !CU_add_test(pSuite, "http2_copy_headers_to_nva",
diff -pruN 1.52.0-1/src/shrpx.cc 1.53.0-1/src/shrpx.cc
--- 1.52.0-1/src/shrpx.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx.cc	2023-05-10 08:27:53.000000000 +0000
@@ -218,23 +218,6 @@ struct WorkerProcess {
         cid_prefixes(cid_prefixes)
 #endif // ENABLE_HTTP3
   {
-    ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
-    reopen_log_signalev.data = this;
-    ev_signal_start(loop, &reopen_log_signalev);
-
-    ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
-    exec_binary_signalev.data = this;
-    ev_signal_start(loop, &exec_binary_signalev);
-
-    ev_signal_init(&graceful_shutdown_signalev, signal_cb,
-                   GRACEFUL_SHUTDOWN_SIGNAL);
-    graceful_shutdown_signalev.data = this;
-    ev_signal_start(loop, &graceful_shutdown_signalev);
-
-    ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
-    reload_signalev.data = this;
-    ev_signal_start(loop, &reload_signalev);
-
     ev_child_init(&worker_process_childev, worker_process_child_cb, worker_pid,
                   0);
     worker_process_childev.data = this;
@@ -242,8 +225,6 @@ struct WorkerProcess {
   }
 
   ~WorkerProcess() {
-    shutdown_signal_watchers();
-
     ev_child_stop(loop, &worker_process_childev);
 
 #ifdef ENABLE_HTTP3
@@ -258,17 +239,6 @@ struct WorkerProcess {
     }
   }
 
-  void shutdown_signal_watchers() {
-    ev_signal_stop(loop, &reopen_log_signalev);
-    ev_signal_stop(loop, &exec_binary_signalev);
-    ev_signal_stop(loop, &graceful_shutdown_signalev);
-    ev_signal_stop(loop, &reload_signalev);
-  }
-
-  ev_signal reopen_log_signalev;
-  ev_signal exec_binary_signalev;
-  ev_signal graceful_shutdown_signalev;
-  ev_signal reload_signalev;
   ev_child worker_process_childev;
   struct ev_loop *loop;
   pid_t worker_pid;
@@ -281,7 +251,7 @@ struct WorkerProcess {
 };
 
 namespace {
-void reload_config(WorkerProcess *wp);
+void reload_config();
 } // namespace
 
 namespace {
@@ -417,18 +387,6 @@ void worker_process_kill(int signum, str
 } // namespace
 
 namespace {
-// Returns the last PID of worker process.  Returns -1 if there is no
-// worker process at the moment.
-int worker_process_last_pid() {
-  if (worker_processes.empty()) {
-    return -1;
-  }
-
-  return worker_processes.back()->worker_pid;
-}
-} // namespace
-
-namespace {
 int save_pid() {
   std::array<char, STRERROR_BUFSIZE> errbuf;
   auto config = get_config();
@@ -712,15 +670,12 @@ void reopen_log(WorkerProcess *wp) {
 
 namespace {
 void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
-  auto wp = static_cast<WorkerProcess *>(w->data);
-  if (wp->worker_pid == -1) {
-    ev_break(loop);
-    return;
-  }
-
   switch (w->signum) {
   case REOPEN_LOG_SIGNAL:
-    reopen_log(wp);
+    for (auto &wp : worker_processes) {
+      reopen_log(wp.get());
+    }
+
     return;
   case EXEC_BINARY_SIGNAL:
     exec_binary();
@@ -730,12 +685,17 @@ void signal_cb(struct ev_loop *loop, ev_
     for (auto &addr : listenerconf.addrs) {
       close(addr.fd);
     }
-    ipc_send(wp, SHRPX_IPC_GRACEFUL_SHUTDOWN);
-    worker_process_set_termination_deadline(wp, loop);
+
+    for (auto &wp : worker_processes) {
+      ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
+      worker_process_set_termination_deadline(wp.get(), loop);
+    }
+
     return;
   }
   case RELOAD_SIGNAL:
-    reload_config(wp);
+    reload_config();
+
     return;
   default:
     worker_process_kill(w->signum, loop);
@@ -751,11 +711,9 @@ void worker_process_child_cb(struct ev_l
 
   log_chld(w->rpid, w->rstatus, "Worker process");
 
-  auto pid = wp->worker_pid;
-
   worker_process_remove(wp, loop);
 
-  if (worker_process_last_pid() == pid) {
+  if (worker_processes.empty()) {
     ev_break(loop);
   }
 }
@@ -1412,6 +1370,30 @@ int create_ipc_socket(std::array<int, 2>
 }
 } // namespace
 
+namespace {
+int create_worker_process_ready_ipc_socket(std::array<int, 2> &ipc_fd) {
+  std::array<char, STRERROR_BUFSIZE> errbuf;
+  int rv;
+
+  rv = socketpair(AF_UNIX, SOCK_DGRAM, 0, ipc_fd.data());
+  if (rv == -1) {
+    auto error = errno;
+    LOG(WARN) << "Failed to create socket pair to communicate worker process "
+                 "readiness: "
+              << xsi_strerror(error, errbuf.data(), errbuf.size());
+    return -1;
+  }
+
+  for (auto fd : ipc_fd) {
+    util::make_socket_closeonexec(fd);
+  }
+
+  util::make_socket_nonblocking(ipc_fd[0]);
+
+  return 0;
+}
+} // namespace
+
 #ifdef ENABLE_HTTP3
 namespace {
 int create_quic_ipc_socket(std::array<int, 2> &quic_ipc_fd) {
@@ -1484,6 +1466,130 @@ collect_quic_lingering_worker_processes(
 #endif // ENABLE_HTTP3
 
 namespace {
+ev_signal reopen_log_signalev;
+ev_signal exec_binary_signalev;
+ev_signal graceful_shutdown_signalev;
+ev_signal reload_signalev;
+} // namespace
+
+namespace {
+void start_signal_watchers(struct ev_loop *loop) {
+  ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
+  ev_signal_start(loop, &reopen_log_signalev);
+
+  ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
+  ev_signal_start(loop, &exec_binary_signalev);
+
+  ev_signal_init(&graceful_shutdown_signalev, signal_cb,
+                 GRACEFUL_SHUTDOWN_SIGNAL);
+  ev_signal_start(loop, &graceful_shutdown_signalev);
+
+  ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
+  ev_signal_start(loop, &reload_signalev);
+}
+} // namespace
+
+namespace {
+void shutdown_signal_watchers(struct ev_loop *loop) {
+  ev_signal_stop(loop, &reload_signalev);
+  ev_signal_stop(loop, &graceful_shutdown_signalev);
+  ev_signal_stop(loop, &exec_binary_signalev);
+  ev_signal_stop(loop, &reopen_log_signalev);
+}
+} // namespace
+
+namespace {
+// A pair of connected socket with which a worker process tells main
+// process that it is ready for service.  A worker process writes its
+// PID to worker_process_ready_ipc_fd[1] and main process reads it
+// from worker_process_ready_ipc_fd[0].
+std::array<int, 2> worker_process_ready_ipc_fd;
+} // namespace
+
+namespace {
+ev_io worker_process_ready_ipcev;
+} // namespace
+
+namespace {
+// PID received via NGHTTPX_ORIG_PID environment variable.
+pid_t orig_pid = -1;
+} // namespace
+
+namespace {
+void worker_process_ready_ipc_readcb(struct ev_loop *loop, ev_io *w,
+                                     int revents) {
+  std::array<uint8_t, 8> buf;
+  ssize_t nread;
+
+  while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR)
+    ;
+
+  if (nread == -1) {
+    std::array<char, STRERROR_BUFSIZE> errbuf;
+    auto error = errno;
+
+    LOG(ERROR) << "Failed to read data from worker process ready IPC channel: "
+               << xsi_strerror(error, errbuf.data(), errbuf.size());
+
+    return;
+  }
+
+  if (nread == 0) {
+    return;
+  }
+
+  if (nread != sizeof(pid_t)) {
+    LOG(ERROR) << "Read " << nread
+               << " bytes from worker process ready IPC channel";
+
+    return;
+  }
+
+  pid_t pid;
+
+  memcpy(&pid, buf.data(), sizeof(pid));
+
+  LOG(NOTICE) << "Worker process pid=" << pid << " is ready";
+
+  for (auto &wp : worker_processes) {
+    // Send graceful shutdown signal to all worker processes prior to
+    // pid.
+    if (wp->worker_pid == pid) {
+      break;
+    }
+
+    LOG(INFO) << "Sending graceful shutdown event to worker process pid="
+              << wp->worker_pid;
+
+    ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
+    worker_process_set_termination_deadline(wp.get(), loop);
+  }
+
+  if (orig_pid != -1) {
+    LOG(NOTICE) << "Send QUIT signal to the original main process to tell "
+                   "that we are ready to serve requests.";
+    kill(orig_pid, SIGQUIT);
+
+    orig_pid = -1;
+  }
+}
+} // namespace
+
+namespace {
+void start_worker_process_ready_ipc_watcher(struct ev_loop *loop) {
+  ev_io_init(&worker_process_ready_ipcev, worker_process_ready_ipc_readcb,
+             worker_process_ready_ipc_fd[0], EV_READ);
+  ev_io_start(loop, &worker_process_ready_ipcev);
+}
+} // namespace
+
+namespace {
+void shutdown_worker_process_ready_ipc_watcher(struct ev_loop *loop) {
+  ev_io_stop(loop, &worker_process_ready_ipcev);
+}
+} // namespace
+
+namespace {
 // Creates worker process, and returns PID of worker process.  On
 // success, file descriptor for IPC (send only) is assigned to
 // |main_ipc_fd|.  In child process, we will close file descriptors
@@ -1545,6 +1651,9 @@ pid_t fork_worker_process(
   }
 
   if (pid == 0) {
+    // We are in new process now, update pid for logger.
+    log_config()->pid = getpid();
+
     ev_loop_fork(EV_DEFAULT);
 
     for (auto &addr : config->conn.listener.addrs) {
@@ -1565,6 +1674,13 @@ pid_t fork_worker_process(
     }
 #endif // ENABLE_HTTP3
 
+    close(worker_process_ready_ipc_fd[0]);
+    shutdown_worker_process_ready_ipc_watcher(EV_DEFAULT);
+
+    if (!config->single_process) {
+      shutdown_signal_watchers(EV_DEFAULT);
+    }
+
     // Remove all WorkerProcesses to stop any registered watcher on
     // default loop.
     worker_process_remove_all(EV_DEFAULT);
@@ -1595,6 +1711,7 @@ pid_t fork_worker_process(
 
     WorkerProcessConfig wpconf{
         .ipc_fd = ipc_fd[0],
+        .ready_ipc_fd = worker_process_ready_ipc_fd[1],
 #ifdef ENABLE_HTTP3
         .cid_prefixes = cid_prefixes,
         .quic_ipc_fd = quic_ipc_fd[0],
@@ -1702,7 +1819,7 @@ int event_loop() {
     close_unused_inherited_addr(iaddrs);
   }
 
-  auto orig_pid = get_orig_pid_from_env();
+  orig_pid = get_orig_pid_from_env();
 
 #ifdef ENABLE_HTTP3
   inherited_quic_lingering_worker_processes =
@@ -1724,6 +1841,13 @@ int event_loop() {
   }
 #endif // ENABLE_HTTP3
 
+  if (!config->single_process) {
+    start_signal_watchers(loop);
+  }
+
+  create_worker_process_ready_ipc_socket(worker_process_ready_ipc_fd);
+  start_worker_process_ready_ipc_watcher(loop);
+
   auto pid = fork_worker_process(ipc_fd
 #ifdef ENABLE_HTTP3
                                  ,
@@ -1759,19 +1883,19 @@ int event_loop() {
     save_pid();
   }
 
-  // ready to serve requests
   shrpx_sd_notifyf(0, "READY=1");
 
-  if (orig_pid != -1) {
-    LOG(NOTICE) << "Send QUIT signal to the original main process to tell "
-                   "that we are ready to serve requests.";
-    kill(orig_pid, SIGQUIT);
-  }
-
   ev_run(loop, 0);
 
   ev_timer_stop(loop, &worker_process_grace_period_timer);
 
+  shutdown_worker_process_ready_ipc_watcher(loop);
+
+  // config is now stale if reload has happened.
+  if (!get_config()->single_process) {
+    shutdown_signal_watchers(loop);
+  }
+
   return 0;
 }
 } // namespace
@@ -3837,7 +3961,7 @@ void close_not_inherited_fd(Config *conf
 } // namespace
 
 namespace {
-void reload_config(WorkerProcess *wp) {
+void reload_config() {
   int rv;
 
   LOG(NOTICE) << "Reloading configuration";
@@ -3916,13 +4040,6 @@ void reload_config(WorkerProcess *wp) {
 
   close_unused_inherited_addr(iaddrs);
 
-  // Send last worker process a graceful shutdown notice
-  auto &last_wp = worker_processes.back();
-  ipc_send(last_wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
-  worker_process_set_termination_deadline(last_wp.get(), loop);
-  // We no longer use signals for this worker.
-  last_wp->shutdown_signal_watchers();
-
   worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd
 #ifdef ENABLE_HTTP3
                                                      ,
diff -pruN 1.52.0-1/src/shrpx_http3_upstream.cc 1.53.0-1/src/shrpx_http3_upstream.cc
--- 1.52.0-1/src/shrpx_http3_upstream.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_http3_upstream.cc	2023-05-10 08:27:53.000000000 +0000
@@ -127,7 +127,7 @@ Http3Upstream::Http3Upstream(ClientHandl
   ev_timer_init(&timer_, timeoutcb, 0., 0.);
   timer_.data = this;
 
-  ngtcp2_connection_close_error_default(&last_error_);
+  ngtcp2_ccerr_default(&last_error_);
 
   ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 0., 0.);
   shutdown_timer_.data = this;
@@ -287,7 +287,7 @@ int Http3Upstream::recv_stream_data(uint
   if (nconsumed < 0) {
     ULOG(ERROR, this) << "nghttp3_conn_read_stream: "
                       << nghttp3_strerror(nconsumed);
-    ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_ccerr_set_application_error(
         &last_error_, nghttp3_err_infer_quic_app_error_code(nconsumed), nullptr,
         0);
     return -1;
@@ -333,7 +333,7 @@ int Http3Upstream::stream_close(int64_t
     break;
   default:
     ULOG(ERROR, this) << "nghttp3_conn_close_stream: " << nghttp3_strerror(rv);
-    ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_ccerr_set_application_error(
         &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr, 0);
     return -1;
   }
@@ -479,19 +479,42 @@ int Http3Upstream::handshake_completed()
     return -1;
   }
 
+  auto path = ngtcp2_conn_get_path(conn_);
+
+  return send_new_token(&path->remote);
+}
+
+namespace {
+int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
+                    const ngtcp2_path *old_path,
+                    ngtcp2_path_validation_result res, void *user_data) {
+  if (res != NGTCP2_PATH_VALIDATION_RESULT_SUCCESS ||
+      !(flags & NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN)) {
+    return 0;
+  }
+
+  auto upstream = static_cast<Http3Upstream *>(user_data);
+  if (upstream->send_new_token(&path->remote) != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  return 0;
+}
+} // namespace
+
+int Http3Upstream::send_new_token(const ngtcp2_addr *remote_addr) {
   std::array<uint8_t, NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN + 1> token;
   size_t tokenlen;
 
-  auto path = ngtcp2_conn_get_path(conn_);
   auto worker = handler_->get_worker();
   auto conn_handler = worker->get_connection_handler();
   auto &qkms = conn_handler->get_quic_keying_materials();
   auto &qkm = qkms->keying_materials.front();
 
-  if (generate_token(token.data(), tokenlen, path->remote.addr,
-                     path->remote.addrlen, qkm.secret.data(),
+  if (generate_token(token.data(), tokenlen, remote_addr->addr,
+                     remote_addr->addrlen, qkm.secret.data(),
                      qkm.secret.size()) != 0) {
-    return 0;
+    return -1;
   }
 
   assert(tokenlen == NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN);
@@ -554,7 +577,7 @@ int Http3Upstream::init(const UpstreamAd
       get_new_connection_id,
       remove_connection_id,
       ngtcp2_crypto_update_key_cb,
-      nullptr, // path_validation
+      shrpx::path_validation,
       nullptr, // select_preferred_addr
       shrpx::stream_reset,
       shrpx::extend_max_remote_streams_bidi,
@@ -629,24 +652,28 @@ int Http3Upstream::init(const UpstreamAd
 
 #ifdef OPENSSL_IS_BORINGSSL
   if (quicconf.upstream.early_data) {
-    ngtcp2_transport_params early_data_params{
-        .initial_max_stream_data_bidi_local =
-            params.initial_max_stream_data_bidi_local,
-        .initial_max_stream_data_bidi_remote =
-            params.initial_max_stream_data_bidi_remote,
-        .initial_max_stream_data_uni = params.initial_max_stream_data_uni,
-        .initial_max_data = params.initial_max_data,
-        .initial_max_streams_bidi = params.initial_max_streams_bidi,
-        .initial_max_streams_uni = params.initial_max_streams_uni,
-    };
+    ngtcp2_transport_params early_data_params;
+
+    ngtcp2_transport_params_default(&early_data_params);
+
+    early_data_params.initial_max_stream_data_bidi_local =
+        params.initial_max_stream_data_bidi_local;
+    early_data_params.initial_max_stream_data_bidi_remote =
+        params.initial_max_stream_data_bidi_remote;
+    early_data_params.initial_max_stream_data_uni =
+        params.initial_max_stream_data_uni;
+    early_data_params.initial_max_data = params.initial_max_data;
+    early_data_params.initial_max_streams_bidi =
+        params.initial_max_streams_bidi;
+    early_data_params.initial_max_streams_uni = params.initial_max_streams_uni;
 
     // TODO include HTTP/3 SETTINGS
 
     std::array<uint8_t, 128> quic_early_data_ctx;
 
-    auto quic_early_data_ctxlen = ngtcp2_encode_transport_params(
+    auto quic_early_data_ctxlen = ngtcp2_transport_params_encode(
         quic_early_data_ctx.data(), quic_early_data_ctx.size(),
-        NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, &early_data_params);
+        &early_data_params);
 
     assert(quic_early_data_ctxlen > 0);
     assert(static_cast<size_t>(quic_early_data_ctxlen) <=
@@ -669,6 +696,8 @@ int Http3Upstream::init(const UpstreamAd
     params.original_dcid = initial_hd.dcid;
   }
 
+  params.original_dcid_present = 1;
+
   rv = generate_quic_stateless_reset_token(
       params.stateless_reset_token, scid, qkm.secret.data(), qkm.secret.size());
   if (rv != 0) {
@@ -728,10 +757,16 @@ int Http3Upstream::on_write() {
     }
   }
 
+  handler_->get_connection()->wlimit.stopw();
+
   if (write_streams() != 0) {
     return -1;
   }
 
+  if (httpconn_ && nghttp3_conn_is_drained(httpconn_)) {
+    return -1;
+  }
+
   reset_timer();
 
   return 0;
@@ -767,7 +802,7 @@ int Http3Upstream::write_streams() {
       if (sveccnt < 0) {
         ULOG(ERROR, this) << "nghttp3_conn_writev_stream: "
                           << nghttp3_strerror(sveccnt);
-        ngtcp2_connection_close_error_set_application_error(
+        ngtcp2_ccerr_set_application_error(
             &last_error_, nghttp3_err_infer_quic_app_error_code(sveccnt),
             nullptr, 0);
         return handle_error();
@@ -802,7 +837,7 @@ int Http3Upstream::write_streams() {
         if (rv != 0) {
           ULOG(ERROR, this)
               << "nghttp3_conn_add_write_offset: " << nghttp3_strerror(rv);
-          ngtcp2_connection_close_error_set_application_error(
+          ngtcp2_ccerr_set_application_error(
               &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
               0);
           return handle_error();
@@ -815,8 +850,7 @@ int Http3Upstream::write_streams() {
       ULOG(ERROR, this) << "ngtcp2_conn_writev_stream: "
                         << ngtcp2_strerror(nwrite);
 
-      ngtcp2_connection_close_error_set_transport_error_liberr(
-          &last_error_, nwrite, nullptr, 0);
+      ngtcp2_ccerr_set_liberr(&last_error_, nwrite, nullptr, 0);
 
       return handle_error();
     } else if (ndatalen >= 0) {
@@ -824,7 +858,7 @@ int Http3Upstream::write_streams() {
       if (rv != 0) {
         ULOG(ERROR, this) << "nghttp3_conn_add_write_offset: "
                           << nghttp3_strerror(rv);
-        ngtcp2_connection_close_error_set_application_error(
+        ngtcp2_ccerr_set_application_error(
             &last_error_, nghttp3_err_infer_quic_app_error_code(rv), nullptr,
             0);
         return handle_error();
@@ -845,18 +879,12 @@ int Http3Upstream::write_streams() {
           on_send_blocked(faddr, prev_ps.path.remote, prev_ps.path.local,
                           prev_pi, data, datalen, gso_size);
 
-          ngtcp2_conn_update_pkt_tx_time(conn_, ts);
-
           signal_write_upstream_addr(faddr);
-
-          return 0;
         }
       }
 
       ngtcp2_conn_update_pkt_tx_time(conn_, ts);
 
-      handler_->get_connection()->wlimit.stopw();
-
       return 0;
     }
 
@@ -902,9 +930,9 @@ int Http3Upstream::write_streams() {
         if (rv == SHRPX_ERR_SEND_BLOCKED) {
           on_send_blocked(faddr, ps.path.remote, ps.path.local, pi, data,
                           nwrite, 0);
-        }
 
-        signal_write_upstream_addr(faddr);
+          signal_write_upstream_addr(faddr);
+        }
       }
       }
 
@@ -924,12 +952,12 @@ int Http3Upstream::write_streams() {
       if (rv == SHRPX_ERR_SEND_BLOCKED) {
         on_send_blocked(faddr, ps.path.remote, ps.path.local, pi, data, datalen,
                         gso_size);
+
+        signal_write_upstream_addr(faddr);
       }
 
       ngtcp2_conn_update_pkt_tx_time(conn_, ts);
 
-      signal_write_upstream_addr(faddr);
-
       return 0;
     }
 #else  // !UDP_SEGMENT
@@ -954,8 +982,6 @@ int Http3Upstream::write_streams() {
     if (++pktcnt == max_pktcnt) {
       ngtcp2_conn_update_pkt_tx_time(conn_, ts);
 
-      signal_write_upstream_addr(faddr);
-
       return 0;
     }
 
@@ -1480,9 +1506,7 @@ void Http3Upstream::on_handler_delete()
     quic_conn_handler->remove_connection_id(cid);
   }
 
-  if (retry_close_ ||
-      last_error_.type ==
-          NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE) {
+  if (retry_close_ || last_error_.type == NGTCP2_CCERR_TYPE_IDLE_CLOSE) {
     return;
   }
 
@@ -1495,8 +1519,13 @@ void Http3Upstream::on_handler_delete()
 
     ngtcp2_path_storage_zero(&ps);
 
-    ngtcp2_connection_close_error ccerr;
-    ngtcp2_connection_close_error_default(&ccerr);
+    ngtcp2_ccerr ccerr;
+    ngtcp2_ccerr_default(&ccerr);
+
+    if (worker->get_graceful_shutdown() &&
+        !ngtcp2_conn_get_handshake_completed(conn_)) {
+      ccerr.error_code = NGTCP2_CONNECTION_REFUSED;
+    }
 
     auto nwrite = ngtcp2_conn_write_connection_close(
         conn_, &ps.path, &pi, conn_close_.data(), conn_close_.size(), &ccerr,
@@ -1759,6 +1788,13 @@ int Http3Upstream::on_read(const Upstrea
       auto worker = handler_->get_worker();
       auto quic_conn_handler = worker->get_quic_connection_handler();
 
+      if (worker->get_graceful_shutdown()) {
+        ngtcp2_ccerr_set_transport_error(&last_error_,
+                                         NGTCP2_CONNECTION_REFUSED, nullptr, 0);
+
+        return handle_error();
+      }
+
       ngtcp2_version_cid vc;
 
       rv =
@@ -1767,19 +1803,6 @@ int Http3Upstream::on_read(const Upstrea
         return -1;
       }
 
-      if (worker->get_graceful_shutdown()) {
-        ngtcp2_cid ini_dcid, ini_scid;
-
-        ngtcp2_cid_init(&ini_dcid, vc.dcid, vc.dcidlen);
-        ngtcp2_cid_init(&ini_scid, vc.scid, vc.scidlen);
-
-        quic_conn_handler->send_connection_close(
-            faddr, vc.version, ini_dcid, ini_scid, remote_addr, local_addr,
-            NGTCP2_CONNECTION_REFUSED, datalen * 3);
-
-        return -1;
-      }
-
       retry_close_ = true;
 
       quic_conn_handler->send_retry(handler_->get_upstream_addr(), vc.version,
@@ -1790,7 +1813,7 @@ int Http3Upstream::on_read(const Upstrea
     }
     case NGTCP2_ERR_CRYPTO:
       if (!last_error_.error_code) {
-        ngtcp2_connection_close_error_set_transport_error_tls_alert(
+        ngtcp2_ccerr_set_tls_alert(
             &last_error_, ngtcp2_conn_get_tls_alert(conn_), nullptr, 0);
       }
       break;
@@ -1798,8 +1821,7 @@ int Http3Upstream::on_read(const Upstrea
       return -1;
     default:
       if (!last_error_.error_code) {
-        ngtcp2_connection_close_error_set_transport_error_liberr(
-            &last_error_, rv, nullptr, 0);
+        ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0);
       }
     }
 
@@ -1952,8 +1974,7 @@ int Http3Upstream::handle_expiry() {
     } else {
       ULOG(ERROR, this) << "ngtcp2_conn_handle_expiry: " << ngtcp2_strerror(rv);
     }
-    ngtcp2_connection_close_error_set_transport_error_liberr(&last_error_, rv,
-                                                             nullptr, 0);
+    ngtcp2_ccerr_set_liberr(&last_error_, rv, nullptr, 0);
     return handle_error();
   }
 
@@ -2564,7 +2585,7 @@ int Http3Upstream::http_reset_stream(int
 int Http3Upstream::setup_httpconn() {
   int rv;
 
-  if (ngtcp2_conn_get_max_local_streams_uni(conn_) < 3) {
+  if (ngtcp2_conn_get_streams_uni_left(conn_) < 3) {
     return -1;
   }
 
@@ -2783,6 +2804,10 @@ int Http3Upstream::start_graceful_shutdo
     return 0;
   }
 
+  if (!httpconn_) {
+    return -1;
+  }
+
   rv = nghttp3_conn_submit_shutdown_notice(httpconn_);
   if (rv != 0) {
     ULOG(FATAL, this) << "nghttp3_conn_submit_shutdown_notice: "
diff -pruN 1.52.0-1/src/shrpx_http3_upstream.h 1.53.0-1/src/shrpx_http3_upstream.h
--- 1.52.0-1/src/shrpx_http3_upstream.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_http3_upstream.h	2023-05-10 08:27:53.000000000 +0000
@@ -153,6 +153,8 @@ public:
 
   ngtcp2_conn *get_conn() const;
 
+  int send_new_token(const ngtcp2_addr *remote_addr);
+
 private:
   ClientHandler *handler_;
   ev_timer timer_;
@@ -161,7 +163,7 @@ private:
   int qlog_fd_;
   ngtcp2_cid hashed_scid_;
   ngtcp2_conn *conn_;
-  ngtcp2_connection_close_error last_error_;
+  ngtcp2_ccerr last_error_;
   nghttp3_conn *httpconn_;
   DownstreamQueue downstream_queue_;
   bool retry_close_;
diff -pruN 1.52.0-1/src/shrpx_https_upstream.cc 1.53.0-1/src/shrpx_https_upstream.cc
--- 1.52.0-1/src/shrpx_https_upstream.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_https_upstream.cc	2023-05-10 08:27:53.000000000 +0000
@@ -658,6 +658,15 @@ int HttpsUpstream::on_read() {
   auto htperr = llhttp_execute(&htp_, reinterpret_cast<const char *>(rb->pos()),
                                rb->rleft());
 
+  if (htperr == HPE_PAUSED_UPGRADE &&
+      rb->pos() ==
+          reinterpret_cast<const uint8_t *>(llhttp_get_error_pos(&htp_))) {
+    llhttp_resume_after_upgrade(&htp_);
+
+    htperr = llhttp_execute(&htp_, reinterpret_cast<const char *>(rb->pos()),
+                            rb->rleft());
+  }
+
   auto nread =
       htperr == HPE_OK
           ? rb->rleft()
diff -pruN 1.52.0-1/src/shrpx_quic_connection_handler.cc 1.53.0-1/src/shrpx_quic_connection_handler.cc
--- 1.52.0-1/src/shrpx_quic_connection_handler.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_quic_connection_handler.cc	2023-05-10 08:27:53.000000000 +0000
@@ -319,21 +319,6 @@ int QUICConnectionHandler::handle_packet
 
       break;
     }
-    case NGTCP2_ERR_RETRY:
-      if (worker_->get_graceful_shutdown()) {
-        send_connection_close(faddr, hd.version, hd.dcid, hd.scid, remote_addr,
-                              local_addr, NGTCP2_CONNECTION_REFUSED,
-                              datalen * 3);
-        return 0;
-      }
-
-      send_retry(faddr, vc.version, vc.dcid, vc.dcidlen, vc.scid, vc.scidlen,
-                 remote_addr, local_addr, datalen * 3);
-      return 0;
-    case NGTCP2_ERR_VERSION_NEGOTIATION:
-      send_version_negotiation(faddr, vc.version, vc.dcid, vc.dcidlen, vc.scid,
-                               vc.scidlen, remote_addr, local_addr);
-      return 0;
     default:
       if (!config->single_thread && !(data[0] & 0x80) &&
           vc.dcidlen == SHRPX_QUIC_SCIDLEN &&
diff -pruN 1.52.0-1/src/shrpx_tls.cc 1.53.0-1/src/shrpx_tls.cc
--- 1.52.0-1/src/shrpx_tls.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_tls.cc	2023-05-10 08:27:53.000000000 +0000
@@ -1813,16 +1813,18 @@ StringRef get_common_name(X509 *cert) {
 }
 } // namespace
 
-namespace {
 int verify_numeric_hostname(X509 *cert, const StringRef &hostname,
                             const Address *addr) {
   const void *saddr;
+  size_t saddrlen;
   switch (addr->su.storage.ss_family) {
   case AF_INET:
     saddr = &addr->su.in.sin_addr;
+    saddrlen = sizeof(addr->su.in.sin_addr);
     break;
   case AF_INET6:
     saddr = &addr->su.in6.sin6_addr;
+    saddrlen = sizeof(addr->su.in6.sin6_addr);
     break;
   default:
     return -1;
@@ -1847,7 +1849,7 @@ int verify_numeric_hostname(X509 *cert,
       size_t ip_addrlen = altname->d.iPAddress->length;
 
       ip_found = true;
-      if (addr->len == ip_addrlen && memcmp(saddr, ip_addr, ip_addrlen) == 0) {
+      if (saddrlen == ip_addrlen && memcmp(saddr, ip_addr, ip_addrlen) == 0) {
         return 0;
       }
     }
@@ -1872,15 +1874,8 @@ int verify_numeric_hostname(X509 *cert,
 
   return -1;
 }
-} // namespace
-
-namespace {
-int verify_hostname(X509 *cert, const StringRef &hostname,
-                    const Address *addr) {
-  if (util::numeric_host(hostname.c_str())) {
-    return verify_numeric_hostname(cert, hostname, addr);
-  }
 
+int verify_dns_hostname(X509 *cert, const StringRef &hostname) {
   auto altnames = static_cast<GENERAL_NAMES *>(
       X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
   if (altnames) {
@@ -1948,6 +1943,16 @@ int verify_hostname(X509 *cert, const St
 
   return rv ? 0 : -1;
 }
+
+namespace {
+int verify_hostname(X509 *cert, const StringRef &hostname,
+                    const Address *addr) {
+  if (util::numeric_host(hostname.c_str())) {
+    return verify_numeric_hostname(cert, hostname, addr);
+  }
+
+  return verify_dns_hostname(cert, hostname);
+}
 } // namespace
 
 int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
diff -pruN 1.52.0-1/src/shrpx_tls.h 1.53.0-1/src/shrpx_tls.h
--- 1.52.0-1/src/shrpx_tls.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_tls.h	2023-05-10 08:27:53.000000000 +0000
@@ -122,6 +122,16 @@ int check_cert(SSL *ssl, const Address *
 // point to &addr->addr.
 int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr);
 
+// Verify |cert| using numeric IP address.  |hostname| and |addr|
+// should contain the same numeric IP address.  This function returns
+// 0 if it succeeds, or -1.
+int verify_numeric_hostname(X509 *cert, const StringRef &hostname,
+                            const Address *addr);
+
+// Verify |cert| using DNS name hostname.  This function returns 0 if
+// it succeeds, or -1.
+int verify_dns_hostname(X509 *cert, const StringRef &hostname);
+
 struct WildcardRevPrefix {
   WildcardRevPrefix(const StringRef &prefix, size_t idx)
       : prefix(std::begin(prefix), std::end(prefix)), idx(idx) {}
diff -pruN 1.52.0-1/src/shrpx_tls_test.cc 1.53.0-1/src/shrpx_tls_test.cc
--- 1.52.0-1/src/shrpx_tls_test.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_tls_test.cc	2023-05-10 08:27:53.000000000 +0000
@@ -194,4 +194,146 @@ void test_shrpx_tls_tls_hostname_match(v
   CU_ASSERT(!tls_hostname_match_wrapper("example.com", "www.example.com"));
 }
 
+static X509 *load_cert(const char *path) {
+  auto f = fopen(path, "r");
+  auto cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
+
+  fclose(f);
+
+  return cert;
+}
+
+static Address parse_addr(const char *ipaddr) {
+  addrinfo hints{};
+
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+  addrinfo *res = nullptr;
+
+  auto rv = getaddrinfo(ipaddr, "443", &hints, &res);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(nullptr != res);
+
+  Address addr;
+  addr.len = res->ai_addrlen;
+  memcpy(&addr.su, res->ai_addr, res->ai_addrlen);
+
+  freeaddrinfo(res);
+
+  return addr;
+}
+
+void test_shrpx_tls_verify_numeric_hostname(void) {
+  {
+    // Successful IPv4 address match in SAN
+    static constexpr char ipaddr[] = "127.0.0.1";
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
+    auto addr = parse_addr(ipaddr);
+    auto rv =
+        tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
+
+    CU_ASSERT(0 == rv);
+
+    X509_free(cert);
+  }
+
+  {
+    // Successful IPv6 address match in SAN
+    static constexpr char ipaddr[] = "::1";
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
+    auto addr = parse_addr(ipaddr);
+    auto rv =
+        tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
+
+    CU_ASSERT(0 == rv);
+
+    X509_free(cert);
+  }
+
+  {
+    // Unsuccessful IPv4 address match in SAN
+    static constexpr char ipaddr[] = "192.168.0.127";
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
+    auto addr = parse_addr(ipaddr);
+    auto rv =
+        tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
+
+    CU_ASSERT(-1 == rv);
+
+    X509_free(cert);
+  }
+
+  {
+    // CommonName is not used if SAN is available
+    static constexpr char ipaddr[] = "192.168.0.1";
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/ipaddr.crt");
+    auto addr = parse_addr(ipaddr);
+    auto rv =
+        tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
+
+    CU_ASSERT(-1 == rv);
+
+    X509_free(cert);
+  }
+
+  {
+    // Successful IPv4 address match in CommonName
+    static constexpr char ipaddr[] = "127.0.0.1";
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/nosan_ip.crt");
+    auto addr = parse_addr(ipaddr);
+    auto rv =
+        tls::verify_numeric_hostname(cert, StringRef::from_lit(ipaddr), &addr);
+
+    CU_ASSERT(0 == rv);
+
+    X509_free(cert);
+  }
+}
+
+void test_shrpx_tls_verify_dns_hostname(void) {
+  {
+    // Successful exact DNS name match in SAN
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
+    auto rv = tls::verify_dns_hostname(
+        cert, StringRef::from_lit("nghttp2.example.com"));
+
+    CU_ASSERT(0 == rv);
+
+    X509_free(cert);
+  }
+
+  {
+    // Successful wildcard DNS name match in SAN
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
+    auto rv = tls::verify_dns_hostname(
+        cert, StringRef::from_lit("www.nghttp2.example.com"));
+
+    CU_ASSERT(0 == rv);
+
+    X509_free(cert);
+  }
+
+  {
+    // CommonName is not used if SAN is available.
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/verify_hostname.crt");
+    auto rv = tls::verify_dns_hostname(cert, StringRef::from_lit("localhost"));
+
+    CU_ASSERT(-1 == rv);
+
+    X509_free(cert);
+  }
+
+  {
+    // Successful DNS name match in CommonName
+    auto cert = load_cert(NGHTTP2_SRC_DIR "/testdata/nosan.crt");
+    auto rv = tls::verify_dns_hostname(cert, StringRef::from_lit("localhost"));
+
+    CU_ASSERT(0 == rv);
+
+    X509_free(cert);
+  }
+}
+
 } // namespace shrpx
diff -pruN 1.52.0-1/src/shrpx_tls_test.h 1.53.0-1/src/shrpx_tls_test.h
--- 1.52.0-1/src/shrpx_tls_test.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_tls_test.h	2023-05-10 08:27:53.000000000 +0000
@@ -34,6 +34,8 @@ namespace shrpx {
 void test_shrpx_tls_create_lookup_tree(void);
 void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void);
 void test_shrpx_tls_tls_hostname_match(void);
+void test_shrpx_tls_verify_numeric_hostname(void);
+void test_shrpx_tls_verify_dns_hostname(void);
 
 } // namespace shrpx
 
diff -pruN 1.52.0-1/src/shrpx_worker_process.cc 1.53.0-1/src/shrpx_worker_process.cc
--- 1.52.0-1/src/shrpx_worker_process.cc	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_worker_process.cc	2023-05-10 08:27:53.000000000 +0000
@@ -405,6 +405,29 @@ void nb_child_cb(struct ev_loop *loop, e
 } // namespace
 #endif // HAVE_NEVERBLEED
 
+namespace {
+int send_ready_event(int ready_ipc_fd) {
+  std::array<char, STRERROR_BUFSIZE> errbuf;
+  auto pid = getpid();
+  ssize_t nwrite;
+
+  while ((nwrite = write(ready_ipc_fd, &pid, sizeof(pid))) == -1 &&
+         errno == EINTR)
+    ;
+
+  if (nwrite < 0) {
+    auto error = errno;
+
+    LOG(ERROR) << "Writing PID to ready IPC channel failed: "
+               << xsi_strerror(error, errbuf.data(), errbuf.size());
+
+    return -1;
+  }
+
+  return 0;
+}
+} // namespace
+
 int worker_process_event_loop(WorkerProcessConfig *wpconf) {
   int rv;
   std::array<char, STRERROR_BUFSIZE> errbuf;
@@ -638,6 +661,10 @@ int worker_process_event_loop(WorkerProc
     LOG(INFO) << "Entering event loop";
   }
 
+  if (send_ready_event(wpconf->ready_ipc_fd) != 0) {
+    return -1;
+  }
+
   ev_run(loop, 0);
 
   conn_handler->cancel_ocsp_update();
diff -pruN 1.52.0-1/src/shrpx_worker_process.h 1.53.0-1/src/shrpx_worker_process.h
--- 1.52.0-1/src/shrpx_worker_process.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/src/shrpx_worker_process.h	2023-05-10 08:27:53.000000000 +0000
@@ -42,6 +42,8 @@ class ConnectionHandler;
 struct WorkerProcessConfig {
   // IPC socket to read event from main process
   int ipc_fd;
+  // IPC socket to tell that a worker process is ready for service.
+  int ready_ipc_fd;
   // IPv4 or UNIX domain socket, or -1 if not used
   int server_fd;
   // IPv6 socket, or -1 if not used
diff -pruN 1.52.0-1/src/testdata/Makefile.am 1.53.0-1/src/testdata/Makefile.am
--- 1.52.0-1/src/testdata/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 1.53.0-1/src/testdata/Makefile.am	2023-05-10 08:27:53.000000000 +0000
@@ -0,0 +1,27 @@
+# nghttp2 - HTTP/2 C Library
+
+# Copyright (c) 2023 Tatsuhiro Tsujikawa
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+EXTRA_DIST = \
+	ipaddr.crt \
+	nosan.crt \
+	nosan_ip.crt \
+	verify_hostname.crt
diff -pruN 1.52.0-1/src/testdata/ipaddr.crt 1.53.0-1/src/testdata/ipaddr.crt
--- 1.52.0-1/src/testdata/ipaddr.crt	1970-01-01 00:00:00.000000000 +0000
+++ 1.53.0-1/src/testdata/ipaddr.crt	2023-05-10 08:27:53.000000000 +0000
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBfDCCASKgAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwsxOTIuMTY4
+LjAuMTAeFw0yMzAzMTUxMjQ5MDBaFw0zMzAxMjExMjQ5MDBaMBYxFDASBgNVBAMT
+CzE5Mi4xNjguMC4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERDh3hNne6xGM
+fOrf7ln5EFnlLpk98qadBx3MKjG5gAfMYHzf/S7v19G608sH1LtabubV+Tvjllon
+K56G2Gk0+6NhMF8wDgYDVR0PAQH/BAQDAgeAME0GA1UdEQRGMESCE25naHR0cDIu
+ZXhhbXBsZS5jb22CFSoubmdodHRwMi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAA
+AAAAAAAAAAAAATAKBggqhkjOPQQDAgNIADBFAiEA3jZzO49MYccR5mYS08qVUCdh
+HsEAC8GhRXFwL6zvf2ACIFAJrca2zTU4QRjV6V+LGRHc2ZocE2e7wFTLobblmDfB
+-----END CERTIFICATE-----
diff -pruN 1.52.0-1/src/testdata/nosan.crt 1.53.0-1/src/testdata/nosan.crt
--- 1.52.0-1/src/testdata/nosan.crt	1970-01-01 00:00:00.000000000 +0000
+++ 1.53.0-1/src/testdata/nosan.crt	2023-05-10 08:27:53.000000000 +0000
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBKDCBz6ADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMTCWxvY2FsaG9z
+dDAeFw0yMzAzMTUxMjQzMzhaFw0zMzAxMjExMjQzMzhaMBQxEjAQBgNVBAMTCWxv
+Y2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIEpWYgtXtcx0uJ2oFPK
+RiII93iw5ITMrhMfBXQ0SzCfkUdvCJ0gNW+3isTBu4Jt0URpgP37eGwiJf2wPApq
+KpajEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNIADBFAiEA4IYil4G4
+cMxaVkcAnMGgiSdn7/qIgdhFB0Vx5AOd+EUCIGubRPhsXAJXvG//cK25mmxi3Wax
+r7AgRKuDtWxn2bCO
+-----END CERTIFICATE-----
diff -pruN 1.52.0-1/src/testdata/nosan_ip.crt 1.53.0-1/src/testdata/nosan_ip.crt
--- 1.52.0-1/src/testdata/nosan_ip.crt	1970-01-01 00:00:00.000000000 +0000
+++ 1.53.0-1/src/testdata/nosan_ip.crt	2023-05-10 08:27:53.000000000 +0000
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBJzCBz6ADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMTCTEyNy4wLjAu
+MTAeFw0yMzAzMTUxMjQ1MTVaFw0zMzAxMjExMjQ1MTVaMBQxEjAQBgNVBAMTCTEy
+Ny4wLjAuMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOXGPfSzXoeD7jszmAQO
+qAhak5HQMTmj32Q/xqO9WmCnXRQ+T06701o6q1hjotrC/HdMk9kabsKHc9V7Bk4O
+zkGjEjAQMA4GA1UdDwEB/wQEAwIHgDAKBggqhkjOPQQDAgNHADBEAiAI3fKrkNTN
+IEo9qI8bd/pZ6on4d9vLcnHtqYhcuWZGTwIgW2zYMwASLUw4H1k/prBtTEEJOahJ
+bvFs3oMbJEprQ+g=
+-----END CERTIFICATE-----
diff -pruN 1.52.0-1/src/testdata/verify_hostname.crt 1.53.0-1/src/testdata/verify_hostname.crt
--- 1.52.0-1/src/testdata/verify_hostname.crt	1970-01-01 00:00:00.000000000 +0000
+++ 1.53.0-1/src/testdata/verify_hostname.crt	2023-05-10 08:27:53.000000000 +0000
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBeTCCAR6gAwIBAgIBATAKBggqhkjOPQQDAjAUMRIwEAYDVQQDEwlsb2NhbGhv
+c3QwHhcNMjMwMzE1MTIzNzU1WhcNMzMwMTIxMTIzNzU1WjAUMRIwEAYDVQQDEwls
+b2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATMHcWmb55fi0KHNDwM
+cYzVTAOfzJf44AqrqC+Pq2zW/ig8tPZbXf3eA/Vvp07Di+yWmuo3fGatUcY4nsx+
+Jd62o2EwXzAOBgNVHQ8BAf8EBAMCB4AwTQYDVR0RBEYwRIITbmdodHRwMi5leGFt
+cGxlLmNvbYIVKi5uZ2h0dHAyLmV4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAA
+AAAAAAABMAoGCCqGSM49BAMCA0kAMEYCIQDQJFRJ3Ah4cGy7bwpkzVYeTgG+NhDa
+55F4dPtJp9dS8wIhALQ9qf379lke1jVHg2t84iZLo3bL23RgICMezEYvqO3K
+-----END CERTIFICATE-----
diff -pruN 1.52.0-1/tests/failmalloc_test.c 1.53.0-1/tests/failmalloc_test.c
--- 1.52.0-1/tests/failmalloc_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/failmalloc_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,11 +24,11 @@
  */
 #include "failmalloc_test.h"
 
-#include <CUnit/CUnit.h>
-
 #include <stdio.h>
 #include <assert.h>
 
+#include <CUnit/CUnit.h>
+
 #include "nghttp2_session.h"
 #include "nghttp2_stream.h"
 #include "nghttp2_frame.h"
diff -pruN 1.52.0-1/tests/main.c 1.53.0-1/tests/main.c
--- 1.52.0-1/tests/main.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/main.c	2023-05-10 08:27:53.000000000 +0000
@@ -449,9 +449,6 @@ int main(void) {
       !CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc) ||
       !CU_add_test(pSuite, "http_parse_priority",
                    test_nghttp2_http_parse_priority) ||
-      !CU_add_test(pSuite, "sf_parse_item", test_nghttp2_sf_parse_item) ||
-      !CU_add_test(pSuite, "sf_parse_inner_list",
-                   test_nghttp2_sf_parse_inner_list) ||
       !CU_add_test(pSuite, "extpri_to_uint8", test_nghttp2_extpri_to_uint8)) {
     CU_cleanup_registry();
     return (int)CU_get_error();
diff -pruN 1.52.0-1/tests/nghttp2_buf_test.c 1.53.0-1/tests/nghttp2_buf_test.c
--- 1.52.0-1/tests/nghttp2_buf_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_buf_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,6 +24,8 @@
  */
 #include "nghttp2_buf_test.h"
 
+#include <stdio.h>
+
 #include <CUnit/CUnit.h>
 
 #include "nghttp2_buf.h"
diff -pruN 1.52.0-1/tests/nghttp2_extpri_test.c 1.53.0-1/tests/nghttp2_extpri_test.c
--- 1.52.0-1/tests/nghttp2_extpri_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_extpri_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -25,6 +25,8 @@
  */
 #include "nghttp2_extpri_test.h"
 
+#include <stdio.h>
+
 #include <CUnit/CUnit.h>
 
 #include "nghttp2_extpri.h"
diff -pruN 1.52.0-1/tests/nghttp2_frame_test.c 1.53.0-1/tests/nghttp2_frame_test.c
--- 1.52.0-1/tests/nghttp2_frame_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_frame_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -68,7 +68,7 @@ static void check_frame_header(size_t le
   CU_ASSERT(0 == hd->reserved);
 }
 
-void test_nghttp2_frame_pack_headers() {
+void test_nghttp2_frame_pack_headers(void) {
   nghttp2_hd_deflater deflater;
   nghttp2_hd_inflater inflater;
   nghttp2_headers frame, oframe;
@@ -276,7 +276,7 @@ void test_nghttp2_frame_pack_rst_stream(
   nghttp2_bufs_free(&bufs);
 }
 
-void test_nghttp2_frame_pack_settings() {
+void test_nghttp2_frame_pack_settings(void) {
   nghttp2_settings frame, oframe;
   nghttp2_bufs bufs;
   int i;
@@ -311,7 +311,7 @@ void test_nghttp2_frame_pack_settings()
   nghttp2_frame_settings_free(&oframe, mem);
 }
 
-void test_nghttp2_frame_pack_push_promise() {
+void test_nghttp2_frame_pack_push_promise(void) {
   nghttp2_hd_deflater deflater;
   nghttp2_hd_inflater inflater;
   nghttp2_push_promise frame, oframe;
@@ -384,7 +384,7 @@ void test_nghttp2_frame_pack_ping(void)
   nghttp2_frame_ping_free(&frame);
 }
 
-void test_nghttp2_frame_pack_goaway() {
+void test_nghttp2_frame_pack_goaway(void) {
   nghttp2_goaway frame, oframe;
   nghttp2_bufs bufs;
   size_t opaque_data_len = 16;
diff -pruN 1.52.0-1/tests/nghttp2_helper_test.c 1.53.0-1/tests/nghttp2_helper_test.c
--- 1.52.0-1/tests/nghttp2_helper_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_helper_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,6 +24,8 @@
  */
 #include "nghttp2_helper_test.h"
 
+#include <stdio.h>
+
 #include <CUnit/CUnit.h>
 
 #include "nghttp2_helper.h"
diff -pruN 1.52.0-1/tests/nghttp2_http_test.c 1.53.0-1/tests/nghttp2_http_test.c
--- 1.52.0-1/tests/nghttp2_http_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_http_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -25,6 +25,7 @@
  */
 #include "nghttp2_http_test.h"
 
+#include <stdio.h>
 #include <assert.h>
 
 #include <CUnit/CUnit.h>
@@ -203,453 +204,3 @@ void test_nghttp2_http_parse_priority(vo
     CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
   }
 }
-
-void test_nghttp2_sf_parse_item(void) {
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "?1";
-    val.type = 0xff;
-
-    CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
-    CU_ASSERT(1 == val.b);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "?1 ";
-    val.type = 0xff;
-
-    CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
-    CU_ASSERT(1 == val.b);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "?1;foo=bar";
-    val.type = 0xff;
-
-    CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
-    CU_ASSERT(1 == val.b);
-  }
-
-  {
-    const uint8_t s[] = {'?', '1', ';', 'f', 'o', 'o', '='};
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s)));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "?0";
-    val.type = 0xff;
-
-    CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
-    CU_ASSERT(0 == val.b);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "?0 ";
-    val.type = 0xff;
-
-    CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
-    CU_ASSERT(0 == val.b);
-  }
-
-  {
-    const uint8_t s[] = "?2";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "?";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "?1";
-
-    CU_ASSERT(2 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:";
-    val.type = 0xff;
-
-    CU_ASSERT(46 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
-    CU_ASSERT(44 == val.s.len);
-    CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
-                          val.s.base, val.s.len));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==: ";
-    val.type = 0xff;
-
-    CU_ASSERT(46 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
-    CU_ASSERT(44 == val.s.len);
-    CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
-                          val.s.base, val.s.len));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "::";
-    val.type = 0xff;
-
-    CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
-    CU_ASSERT(0 == val.s.len);
-  }
-
-  {
-    const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = ":";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = ":@:";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = ":foo:";
-
-    CU_ASSERT(5 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] =
-        ":abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=:";
-    val.type = 0xff;
-
-    CU_ASSERT(67 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
-    CU_ASSERT(65 == val.s.len);
-    CU_ASSERT(
-        0 ==
-        memcmp(
-            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=",
-            val.s.base, val.s.len));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "foo123/456";
-    val.type = 0xff;
-
-    CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
-    CU_ASSERT(10 == val.s.len);
-    CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "foo123/456 ";
-    val.type = 0xff;
-
-    CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
-    CU_ASSERT(10 == val.s.len);
-    CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "*";
-    val.type = 0xff;
-
-    CU_ASSERT(1 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
-    CU_ASSERT(1 == val.s.len);
-    CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
-  }
-
-  {
-    const uint8_t s[] = "*";
-
-    CU_ASSERT(1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "\"hello world\"";
-    val.type = 0xff;
-
-    CU_ASSERT(13 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
-    CU_ASSERT(11 == val.s.len);
-    CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "\"hello world\" ";
-    val.type = 0xff;
-
-    CU_ASSERT(13 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
-    CU_ASSERT(11 == val.s.len);
-    CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "\"foo\\\"\\\\\"";
-    val.type = 0xff;
-
-    CU_ASSERT(9 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
-    CU_ASSERT(7 == val.s.len);
-    CU_ASSERT(0 == memcmp("foo\\\"\\\\", val.s.base, val.s.len));
-  }
-
-  {
-    const uint8_t s[] = "\"foo\\x\"";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "\"foo";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "\"\x7f\"";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "\"\x1f\"";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "\"foo\"";
-
-    CU_ASSERT(5 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "4.5";
-    val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
-
-    CU_ASSERT(3 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
-    CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "4.5 ";
-    val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
-
-    CU_ASSERT(3 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
-    CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "-4.5";
-    val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
-
-    CU_ASSERT(4 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
-    CU_ASSERT(fabs(-4.5 - val.d) < 1e-9);
-  }
-
-  {
-    const uint8_t s[] = "4.5";
-
-    CU_ASSERT(3 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "123456789012.123";
-    val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
-
-    CU_ASSERT(16 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
-    CU_ASSERT(fabs(123456789012.123 - val.d) < 1e-9);
-  }
-
-  {
-    const uint8_t s[] = "1123456789012.123";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "123456789012.1234";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "1.";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "123456789012345";
-    val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
-
-    CU_ASSERT(15 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INTEGER == val.type);
-    CU_ASSERT(123456789012345 == val.i);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "1 ";
-    val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
-
-    CU_ASSERT(1 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INTEGER == val.type);
-    CU_ASSERT(1 == val.i);
-  }
-
-  {
-    const uint8_t s[] = "1";
-
-    CU_ASSERT(1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "1234567890123456";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "\"foo\";a;  b=\"bar\";c=1.3;d=9;e=baz;f=:aaa:";
-    val.type = 0xff;
-
-    CU_ASSERT(41 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
-    CU_ASSERT(0 == memcmp("foo", val.s.base, val.s.len));
-  }
-
-  {
-    const uint8_t s[] = "\"foo\";a;  b=\"bar";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "foo;";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
-  }
-}
-
-void test_nghttp2_sf_parse_inner_list(void) {
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "()";
-    val.type = 0xff;
-
-    CU_ASSERT(2 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "(     )";
-    val.type = 0xff;
-
-    CU_ASSERT(7 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "(a)";
-    val.type = 0xff;
-
-    CU_ASSERT(3 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "(a b)";
-    val.type = 0xff;
-
-    CU_ASSERT(5 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "(  a b   )";
-    val.type = 0xff;
-
-    CU_ASSERT(10 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
-  }
-
-  {
-    nghttp2_sf_value val;
-    const uint8_t s[] = "( a;foo=bar)";
-    val.type = 0xff;
-
-    CU_ASSERT(12 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
-    CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
-  }
-
-  {
-    const uint8_t s[] = "(";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "(a";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "(a   ";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
-  }
-
-  {
-    const uint8_t s[] = "(a;b";
-
-    CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
-  }
-}
diff -pruN 1.52.0-1/tests/nghttp2_http_test.h 1.53.0-1/tests/nghttp2_http_test.h
--- 1.52.0-1/tests/nghttp2_http_test.h	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_http_test.h	2023-05-10 08:27:53.000000000 +0000
@@ -31,7 +31,5 @@
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_http_parse_priority(void);
-void test_nghttp2_sf_parse_item(void);
-void test_nghttp2_sf_parse_inner_list(void);
 
 #endif /* NGHTTP2_HTTP_TEST_H */
diff -pruN 1.52.0-1/tests/nghttp2_map_test.c 1.53.0-1/tests/nghttp2_map_test.c
--- 1.52.0-1/tests/nghttp2_map_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_map_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -25,6 +25,8 @@
  */
 #include "nghttp2_map_test.h"
 
+#include <stdio.h>
+
 #include <CUnit/CUnit.h>
 
 #include "nghttp2_map.h"
diff -pruN 1.52.0-1/tests/nghttp2_npn_test.c 1.53.0-1/tests/nghttp2_npn_test.c
--- 1.52.0-1/tests/nghttp2_npn_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_npn_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,6 +24,7 @@
  */
 #include "nghttp2_npn_test.h"
 
+#include <stdio.h>
 #include <string.h>
 
 #include <CUnit/CUnit.h>
diff -pruN 1.52.0-1/tests/nghttp2_pq_test.c 1.53.0-1/tests/nghttp2_pq_test.c
--- 1.52.0-1/tests/nghttp2_pq_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_pq_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,6 +24,8 @@
  */
 #include "nghttp2_pq_test.h"
 
+#include <stdio.h>
+
 #include <CUnit/CUnit.h>
 
 #include "nghttp2_pq.h"
diff -pruN 1.52.0-1/tests/nghttp2_queue_test.c 1.53.0-1/tests/nghttp2_queue_test.c
--- 1.52.0-1/tests/nghttp2_queue_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_queue_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,6 +24,8 @@
  */
 #include "nghttp2_queue_test.h"
 
+#include <stdio.h>
+
 #include <CUnit/CUnit.h>
 
 #include "nghttp2_queue.h"
diff -pruN 1.52.0-1/tests/nghttp2_session_test.c 1.53.0-1/tests/nghttp2_session_test.c
--- 1.52.0-1/tests/nghttp2_session_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_session_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -572,8 +572,7 @@ static ssize_t defer_data_source_read_ca
 }
 
 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
-                                    nghttp2_error_code error_code,
-                                    void *user_data) {
+                                    uint32_t error_code, void *user_data) {
   my_user_data *my_data = (my_user_data *)user_data;
   (void)session;
   (void)stream_id;
diff -pruN 1.52.0-1/tests/nghttp2_stream_test.c 1.53.0-1/tests/nghttp2_stream_test.c
--- 1.52.0-1/tests/nghttp2_stream_test.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_stream_test.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,6 +24,8 @@
  */
 #include "nghttp2_stream_test.h"
 
+#include <stdio.h>
+
 #include <CUnit/CUnit.h>
 
 #include "nghttp2_stream.h"
diff -pruN 1.52.0-1/tests/nghttp2_test_helper.c 1.53.0-1/tests/nghttp2_test_helper.c
--- 1.52.0-1/tests/nghttp2_test_helper.c	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/tests/nghttp2_test_helper.c	2023-05-10 08:27:53.000000000 +0000
@@ -24,6 +24,7 @@
  */
 #include "nghttp2_test_helper.h"
 
+#include <stdio.h>
 #include <assert.h>
 
 #include <CUnit/CUnit.h>
diff -pruN 1.52.0-1/third-party/Makefile.am 1.53.0-1/third-party/Makefile.am
--- 1.52.0-1/third-party/Makefile.am	2023-02-13 11:59:29.000000000 +0000
+++ 1.53.0-1/third-party/Makefile.am	2023-05-10 08:27:53.000000000 +0000
@@ -100,6 +100,7 @@ EXTRA_DIST += \
 	mruby/lib/mruby/lockfile.rb \
 	mruby/lib/mruby/build.rb \
 	mruby/lib/mruby/core_ext.rb \
+	mruby/lib/mruby/doc.rb \
 	mruby/lib/mruby/gem.rb \
 	mruby/lib/mruby/presym.rb \
 	mruby/lib/mruby/source.rb \
@@ -107,6 +108,10 @@ EXTRA_DIST += \
 	mruby/examples/mrbgems/ruby_extension_example/README.md \
 	mruby/examples/mrbgems/ruby_extension_example/test/example.rb \
 	mruby/examples/mrbgems/ruby_extension_example/mrblib/example.rb \
+	mruby/examples/mrbgems/cdata_extension_example/mrbgem.rake \
+	mruby/examples/mrbgems/cdata_extension_example/README.md \
+	mruby/examples/mrbgems/cdata_extension_example/test/example.c \
+	mruby/examples/mrbgems/cdata_extension_example/src/example.c \
 	mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake \
 	mruby/examples/mrbgems/c_and_ruby_extension_example/README.md \
 	mruby/examples/mrbgems/c_and_ruby_extension_example/test/example.rb \
@@ -117,6 +122,9 @@ EXTRA_DIST += \
 	mruby/examples/mrbgems/c_extension_example/test/example.rb \
 	mruby/examples/mrbgems/c_extension_example/test/example.c \
 	mruby/examples/mrbgems/c_extension_example/src/example.c \
+	mruby/examples/mrbgems/mruby-YOUR-bigint/TODO-HINT.md \
+	mruby/examples/mrbgems/mruby-YOUR-bigint/core/bigint.c \
+	mruby/examples/mrbgems/mruby-YOUR-bigint/mrbgem.rake \
 	mruby/test/bintest.rb \
 	mruby/test/t/enumerable.rb \
 	mruby/test/t/comparable.rb \
@@ -162,8 +170,10 @@ EXTRA_DIST += \
 	mruby/test/t/standarderror.rb \
 	mruby/test/t/superclass.rb \
 	mruby/test/assert.rb \
+	mruby/super-linter.report/.keep \
 	mruby/TODO.md \
 	mruby/Doxyfile \
+	mruby/mrblib/00kernel.rb \
 	mruby/mrblib/range.rb \
 	mruby/mrblib/kernel.rb \
 	mruby/mrblib/hash.rb \
@@ -186,12 +196,15 @@ EXTRA_DIST += \
 	mruby/src/load.c \
 	mruby/src/codedump.c \
 	mruby/src/error.c \
+	mruby/src/readfloat.c \
 	mruby/src/state.c \
 	mruby/src/string.c \
 	mruby/src/numeric.c \
+	mruby/src/readnum.c \
 	mruby/src/enum.c \
 	mruby/src/print.c \
 	mruby/src/readint.c \
+	mruby/src/numops.c \
 	mruby/src/hash.c \
 	mruby/src/version.c \
 	mruby/src/backtrace.c \
@@ -210,7 +223,6 @@ EXTRA_DIST += \
 	mruby/src/compar.c \
 	mruby/src/value_array.h \
 	mruby/src/pool.c \
-	mruby/src/readflt.c \
 	mruby/mruby-source.gemspec \
 	mruby/mrbgems/mruby-eval/mrbgem.rake \
 	mruby/mrbgems/mruby-eval/test/eval.rb \
@@ -223,6 +235,12 @@ EXTRA_DIST += \
 	mruby/mrbgems/mruby-string-ext/mrblib/string.rb \
 	mruby/mrbgems/mruby-string-ext/src/string.c \
 	mruby/mrbgems/default-no-stdio.gembox \
+	mruby/mrbgems/mruby-set/mrbgem.rake \
+	mruby/mrbgems/mruby-set/README.md \
+	mruby/mrbgems/mruby-set/test/set.rb \
+	mruby/mrbgems/mruby-set/mrblib/set.rb \
+	mruby/mrbgems/mruby-set/LICENSE \
+	mruby/mrbgems/mruby-set/mruby-set.gem \
 	mruby/mrbgems/mruby-method/mrbgem.rake \
 	mruby/mrbgems/mruby-method/README.md \
 	mruby/mrbgems/mruby-method/test/method.rb \
@@ -249,6 +267,12 @@ EXTRA_DIST += \
 	mruby/mrbgems/mruby-struct/test/struct.rb \
 	mruby/mrbgems/mruby-struct/mrblib/struct.rb \
 	mruby/mrbgems/mruby-struct/src/struct.c \
+	mruby/mrbgems/mruby-bigint/core/bigint.c \
+	mruby/mrbgems/mruby-bigint/core/bigint.h \
+	mruby/mrbgems/mruby-bigint/mrbgem.rake \
+	mruby/mrbgems/mruby-bigint/README.md \
+	mruby/mrbgems/mruby-bigint/test/bigint.rb \
+	mruby/mrbgems/mruby-bigint/README-fgmp.md \
 	mruby/mrbgems/mruby-symbol-ext/mrbgem.rake \
 	mruby/mrbgems/mruby-symbol-ext/test/symbol.rb \
 	mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb \
@@ -283,6 +307,16 @@ EXTRA_DIST += \
 	mruby/mrbgems/mruby-proc-ext/test/proc.c \
 	mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb \
 	mruby/mrbgems/mruby-proc-ext/src/proc.c \
+	mruby/mrbgems/mruby-data/mrbgem.rake \
+	mruby/mrbgems/mruby-data/test/data.rb \
+	mruby/mrbgems/mruby-data/src/data.c \
+	mruby/mrbgems/mruby-dir/mrbgem.rake \
+	mruby/mrbgems/mruby-dir/README.md \
+	mruby/mrbgems/mruby-dir/test/dir.rb \
+	mruby/mrbgems/mruby-dir/test/dirtest.c \
+	mruby/mrbgems/mruby-dir/mrblib/dir.rb \
+	mruby/mrbgems/mruby-dir/src/Win/dirent.c \
+	mruby/mrbgems/mruby-dir/src/dir.c \
 	mruby/mrbgems/mruby-object-ext/mrbgem.rake \
 	mruby/mrbgems/mruby-object-ext/test/nil.rb \
 	mruby/mrbgems/mruby-object-ext/test/object.rb \
@@ -292,6 +326,7 @@ EXTRA_DIST += \
 	mruby/mrbgems/mruby-kernel-ext/test/kernel.rb \
 	mruby/mrbgems/mruby-kernel-ext/src/kernel.c \
 	mruby/mrbgems/mruby-class-ext/mrbgem.rake \
+	mruby/mrbgems/mruby-class-ext/test/class.rb \
 	mruby/mrbgems/mruby-class-ext/test/module.rb \
 	mruby/mrbgems/mruby-class-ext/mrblib/module.rb \
 	mruby/mrbgems/mruby-class-ext/src/class.c \
@@ -425,12 +460,21 @@ EXTRA_DIST += \
 	mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c \
 	mruby/mrbgems/mruby-bin-strip/mrbgem.rake \
 	mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb \
+	mruby/mrbgems/mruby-errno/mrbgem.rake \
+	mruby/mrbgems/mruby-errno/README.md \
+	mruby/mrbgems/mruby-errno/test/errno.rb \
+	mruby/mrbgems/mruby-errno/mrblib/errno.rb \
+	mruby/mrbgems/mruby-errno/src/gen.rb \
+	mruby/mrbgems/mruby-errno/src/known_errors_def.cstub \
+	mruby/mrbgems/mruby-errno/src/known_errors.def \
+	mruby/mrbgems/mruby-errno/src/errno.c \
 	mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake \
 	mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb \
 	mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb \
 	mruby/mrbgems/mruby-math/mrbgem.rake \
 	mruby/mrbgems/mruby-math/test/math.rb \
 	mruby/mrbgems/mruby-math/src/math.c \
+	mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/stub.c \
 	mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c \
 	mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake \
 	mruby/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb \
@@ -451,6 +495,7 @@ EXTRA_DIST += \
 	mruby/doc/internal/opcode.md \
 	mruby/doc/internal/boxing.md \
 	mruby/doc/limitations.md \
+	mruby/doc/mruby3.2.md \
 	mruby/doc/guides/compile.md \
 	mruby/doc/guides/mrbgems.md \
 	mruby/doc/guides/debugger.md \
@@ -484,6 +529,7 @@ EXTRA_DIST += \
 	mruby/include/mruby/boxing_nan.h \
 	mruby/include/mruby/value.h \
 	mruby/include/mruby/endian.h \
+	mruby/include/mruby/internal.h \
 	mruby/include/mruby/dump.h \
 	mruby/include/mruby/version.h \
 	mruby/include/mruby/khash.h \
