summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:32:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:32:08 +0000
commit328ad0a41c6bdf596224ff2e9ab9c0fabde8634d (patch)
tree973585a56cea8664b4be63b5bb737b443e4e2b76
parentInitial commit. (diff)
downloadnghttp3-328ad0a41c6bdf596224ff2e9ab9c0fabde8634d.tar.xz
nghttp3-328ad0a41c6bdf596224ff2e9ab9c0fabde8634d.zip
Adding upstream version 0.8.0.upstream/0.8.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.clang-format191
-rw-r--r--.clusterfuzzlite/Dockerfile5
-rwxr-xr-x.clusterfuzzlite/build.sh16
-rw-r--r--.clusterfuzzlite/project.yaml1
-rw-r--r--.github/workflows/build.yml153
-rw-r--r--.github/workflows/cflite_batch.yaml29
-rw-r--r--.github/workflows/cflite_cron.yaml19
-rw-r--r--.gitignore38
-rw-r--r--AUTHORS17
-rw-r--r--CMakeLists.txt279
-rw-r--r--CMakeOptions.txt11
-rw-r--r--COPYING22
-rw-r--r--ChangeLog0
-rw-r--r--Makefile.am45
-rw-r--r--NEWS0
-rw-r--r--README1
-rw-r--r--README.rst42
-rw-r--r--cmake/ExtractValidFlags.cmake31
-rw-r--r--cmake/FindCUnit.cmake40
-rw-r--r--cmake/Version.cmake11
-rw-r--r--cmakeconfig.h.in33
-rw-r--r--configure.ac353
-rw-r--r--doc/.gitignore1
-rw-r--r--doc/Makefile.am51
-rw-r--r--doc/make.bat35
-rwxr-xr-xdoc/mkapiref.py358
-rw-r--r--doc/source/.gitignore5
-rw-r--r--doc/source/conf.py.in94
-rw-r--r--doc/source/index.rst22
-rw-r--r--doc/source/programmers-guide.rst207
-rw-r--r--doc/source/qpack-howto.rst85
-rw-r--r--examples/.gitignore1
-rw-r--r--examples/CMakeLists.txt50
-rw-r--r--examples/Makefile.am46
-rw-r--r--examples/qpack.cc143
-rw-r--r--examples/qpack.h44
-rw-r--r--examples/qpack_decode.cc299
-rw-r--r--examples/qpack_decode.h93
-rw-r--r--examples/qpack_encode.cc221
-rw-r--r--examples/qpack_encode.h59
-rw-r--r--examples/template.h77
-rw-r--r--examples/util.cc27
-rw-r--r--examples/util.h64
-rw-r--r--fuzz/corpus/fuzz_http3serverreq/curlbin0 -> 1110 bytes
-rw-r--r--fuzz/corpus/fuzz_qpackdecoder/netbsd-hq.out.256.100.1bin0 -> 1998 bytes
-rw-r--r--fuzz/fuzz_http3serverreq.cc67
-rw-r--r--fuzz/fuzz_qpackdecoder.cc250
-rwxr-xr-xgenchartbl.py66
-rwxr-xr-xgenlibtokenlookup.py185
-rw-r--r--lib/CMakeLists.txt97
-rw-r--r--lib/Makefile.am97
-rw-r--r--lib/includes/CMakeLists.txt4
-rw-r--r--lib/includes/Makefile.am26
-rw-r--r--lib/includes/nghttp3/nghttp3.h2646
-rw-r--r--lib/includes/nghttp3/version.h.in46
-rw-r--r--lib/libnghttp3.pc.in34
-rw-r--r--lib/nghttp3_balloc.c91
-rw-r--r--lib/nghttp3_balloc.h92
-rw-r--r--lib/nghttp3_buf.c90
-rw-r--r--lib/nghttp3_buf.h74
-rw-r--r--lib/nghttp3_conn.c2532
-rw-r--r--lib/nghttp3_conn.h200
-rw-r--r--lib/nghttp3_conv.c125
-rw-r--r--lib/nghttp3_conv.h207
-rw-r--r--lib/nghttp3_debug.c61
-rw-r--r--lib/nghttp3_debug.h44
-rw-r--r--lib/nghttp3_err.c126
-rw-r--r--lib/nghttp3_err.h34
-rw-r--r--lib/nghttp3_frame.c204
-rw-r--r--lib/nghttp3_frame.h214
-rw-r--r--lib/nghttp3_gaptr.c169
-rw-r--r--lib/nghttp3_gaptr.h99
-rw-r--r--lib/nghttp3_http.c1654
-rw-r--r--lib/nghttp3_http.h202
-rw-r--r--lib/nghttp3_idtr.c80
-rw-r--r--lib/nghttp3_idtr.h90
-rw-r--r--lib/nghttp3_ksl.c827
-rw-r--r--lib/nghttp3_ksl.h348
-rw-r--r--lib/nghttp3_macro.h51
-rw-r--r--lib/nghttp3_map.c337
-rw-r--r--lib/nghttp3_map.h137
-rw-r--r--lib/nghttp3_mem.c124
-rw-r--r--lib/nghttp3_mem.h80
-rw-r--r--lib/nghttp3_objalloc.c41
-rw-r--r--lib/nghttp3_objalloc.h141
-rw-r--r--lib/nghttp3_opl.c47
-rw-r--r--lib/nghttp3_opl.h66
-rw-r--r--lib/nghttp3_pq.c168
-rw-r--r--lib/nghttp3_pq.h129
-rw-r--r--lib/nghttp3_qpack.c4093
-rw-r--r--lib/nghttp3_qpack.h996
-rw-r--r--lib/nghttp3_qpack_huffman.c122
-rw-r--r--lib/nghttp3_qpack_huffman.h108
-rw-r--r--lib/nghttp3_qpack_huffman_data.c4981
-rw-r--r--lib/nghttp3_range.c62
-rw-r--r--lib/nghttp3_range.h81
-rw-r--r--lib/nghttp3_rcbuf.c108
-rw-r--r--lib/nghttp3_rcbuf.h81
-rw-r--r--lib/nghttp3_ringbuf.c159
-rw-r--r--lib/nghttp3_ringbuf.h113
-rw-r--r--lib/nghttp3_str.c110
-rw-r--r--lib/nghttp3_str.h40
-rw-r--r--lib/nghttp3_stream.c1248
-rw-r--r--lib/nghttp3_stream.h396
-rw-r--r--lib/nghttp3_tnode.c97
-rw-r--r--lib/nghttp3_tnode.h66
-rw-r--r--lib/nghttp3_unreachable.c72
-rw-r--r--lib/nghttp3_unreachable.h47
-rw-r--r--lib/nghttp3_vec.c55
-rw-r--r--lib/nghttp3_vec.h41
-rw-r--r--lib/nghttp3_version.c39
-rw-r--r--m4/ax_check_compile_flag.m474
-rw-r--r--m4/ax_cxx_compile_stdcxx.m4948
-rwxr-xr-xmkhufftbl.py468
-rwxr-xr-xmkstatichdtbl.py90
-rwxr-xr-xqifs-check.sh23
-rwxr-xr-xqifs.sh19
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/CMakeLists.txt57
-rw-r--r--tests/Makefile.am65
-rw-r--r--tests/main.c146
-rw-r--r--tests/nghttp3_conn_test.c3448
-rw-r--r--tests/nghttp3_conn_test.h58
-rw-r--r--tests/nghttp3_conv_test.c51
-rw-r--r--tests/nghttp3_conv_test.h34
-rw-r--r--tests/nghttp3_http_test.c676
-rw-r--r--tests/nghttp3_http_test.h37
-rw-r--r--tests/nghttp3_qpack_test.c779
-rw-r--r--tests/nghttp3_qpack_test.h41
-rw-r--r--tests/nghttp3_test_helper.c139
-rw-r--r--tests/nghttp3_test_helper.h72
-rw-r--r--tests/nghttp3_tnode_test.c164
-rw-r--r--tests/nghttp3_tnode_test.h34
133 files changed, 36260 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..b4380bc
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,191 @@
+---
+Language: Cpp
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignArrayOfStructures: None
+AlignConsecutiveMacros: None
+AlignConsecutiveAssignments: None
+AlignConsecutiveBitFields: None
+AlignConsecutiveDeclarations: None
+AlignEscapedNewlines: Right
+AlignOperands: Align
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortEnumsOnASingleLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortLambdasOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: MultiLine
+AttributeMacros:
+ - __capability
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: Never
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeConceptDeclarations: true
+BreakBeforeBraces: Attach
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+QualifierAlignment: Leave
+CompactNamespaces: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+PackConstructorInitializers: NextLine
+BasedOnStyle: ''
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+AllowAllConstructorInitializersOnNextLine: true
+FixNamespaceComments: true
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^(<|"(gtest|isl|json)/)'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '$'
+IncludeIsMainSourceRegex: ''
+IndentAccessModifiers: false
+IndentCaseLabels: false
+IndentCaseBlocks: false
+IndentGotoLabels: true
+IndentPPDirectives: AfterHash
+IndentExternBlock: AfterExternBlock
+IndentRequires: false
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+LambdaBodyIndentation: Signature
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PenaltyIndentedWhitespace: 0
+PointerAlignment: Right
+PPIndentWidth: -1
+ReferenceAlignment: Pointer
+ReflowComments: true
+RemoveBracesLLVM: false
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: Never
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDefinitionName: false
+ AfterFunctionDeclarationName: false
+ AfterIfMacros: true
+ AfterOverloadedOperator: false
+ BeforeNonEmptyParentheses: false
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+SpaceBeforeSquareBrackets: false
+BitFieldColonSpacing: Both
+Standard: Latest
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 8
+UseCRLF: false
+UseTab: Never
+WhitespaceSensitiveMacros:
+ - STRINGIZE
+ - PP_STRINGIZE
+ - BOOST_PP_STRINGIZE
+ - NS_SWIFT_NAME
+ - CF_SWIFT_NAME
+...
+
diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile
new file mode 100644
index 0000000..86aa048
--- /dev/null
+++ b/.clusterfuzzlite/Dockerfile
@@ -0,0 +1,5 @@
+FROM gcr.io/oss-fuzz-base/base-builder:v1
+RUN apt-get update && apt-get install -y make autoconf automake libtool pkg-config
+COPY . $SRC/nghttp3
+WORKDIR nghttp3
+COPY .clusterfuzzlite/build.sh $SRC/
diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh
new file mode 100755
index 0000000..5c68ce5
--- /dev/null
+++ b/.clusterfuzzlite/build.sh
@@ -0,0 +1,16 @@
+#!/bin/bash -eu
+
+autoreconf -i
+./configure
+make -j$(nproc)
+
+$CXX $CXXFLAGS -std=c++17 -Ilib/includes \
+ fuzz/fuzz_http3serverreq.cc -o $OUT/fuzz_http3serverreq \
+ $LIB_FUZZING_ENGINE lib/.libs/libnghttp3.a
+
+$CXX $CXXFLAGS -std=c++17 -Ilib/includes \
+ fuzz/fuzz_qpackdecoder.cc -o $OUT/fuzz_qpackdecoder \
+ $LIB_FUZZING_ENGINE lib/.libs/libnghttp3.a
+
+zip -j $OUT/fuzz_http3serverreq_seed_corpus.zip fuzz/corpus/fuzz_http3serverreq/*
+zip -j $OUT/fuzz_qpackdecoder_seed_corpus.zip fuzz/corpus/fuzz_qpackdecoder/*
diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml
new file mode 100644
index 0000000..b478801
--- /dev/null
+++ b/.clusterfuzzlite/project.yaml
@@ -0,0 +1 @@
+language: c++
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..a250041
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,153 @@
+name: build
+
+on: [push, pull_request]
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ os: [ubuntu-22.04, macos-11]
+ compiler: [gcc, clang]
+ buildtool: [autotools, distcheck, cmake]
+ exclude:
+ - os: macos-11
+ buildtool: distcheck
+ - compiler: gcc
+ buildtool: distcheck
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Linux setup
+ if: runner.os == 'Linux'
+ run: |
+ sudo apt-get install \
+ g++-12 \
+ clang-14 \
+ autoconf \
+ automake \
+ autotools-dev \
+ libtool \
+ pkg-config \
+ libcunit1-dev \
+ cmake \
+ cmake-data
+ - name: MacOS setup
+ if: runner.os == 'macOS'
+ run: |
+ brew install cunit autoconf automake pkg-config libtool
+ - name: Setup clang (Linux)
+ if: runner.os == 'Linux' && matrix.compiler == 'clang'
+ run: |
+ echo 'CC=clang-14' >> $GITHUB_ENV
+ echo 'CXX=clang++-14' >> $GITHUB_ENV
+ - name: Setup clang (MacOS)
+ if: runner.os == 'macOS' && matrix.compiler == 'clang'
+ run: |
+ echo 'CC=clang' >> $GITHUB_ENV
+ echo 'CXX=clang++' >> $GITHUB_ENV
+ - name: Setup gcc (Linux)
+ if: runner.os == 'Linux' && matrix.compiler == 'gcc'
+ run: |
+ echo 'CC=gcc-12' >> $GITHUB_ENV
+ echo 'CXX=g++-12' >> $GITHUB_ENV
+ - name: Setup gcc (MacOS)
+ if: runner.os == 'macOS' && matrix.compiler == 'gcc'
+ run: |
+ echo 'CC=gcc' >> $GITHUB_ENV
+ echo 'CXX=g++' >> $GITHUB_ENV
+ - name: Configure autotools
+ if: matrix.buildtool == 'autotools'
+ run: |
+ autoreconf -i && ./configure --enable-werror
+ - name: Configure distcheck
+ if: matrix.buildtool == 'distcheck'
+ run: |
+ autoreconf -i && ./configure
+ - name: Configure cmake
+ if: matrix.buildtool == 'cmake'
+ run: |
+ autoreconf -i && ./configure
+ make dist
+
+ VERSION=$(grep PACKAGE_VERSION config.h | cut -d' ' -f3 | tr -d '"')
+ tar xf nghttp3-$VERSION.tar.gz
+ cd nghttp3-$VERSION
+ mkdir build
+ cd build
+
+ echo 'NGHTTP3_BUILD_DIR='"$PWD" >> $GITHUB_ENV
+
+ cmake $CMAKE_OPTS ..
+ - name: Build nghttp3
+ if: matrix.buildtool != 'distcheck'
+ run: |
+ [ -n "$NGHTTP3_BUILD_DIR" ] && cd "$NGHTTP3_BUILD_DIR"
+ make
+ make check
+ - name: Build nghttp3 with distcheck
+ if: matrix.buildtool == 'distcheck'
+ run: |
+ make distcheck
+
+ build-cross:
+ strategy:
+ matrix:
+ host: [x86_64-w64-mingw32, i686-w64-mingw32]
+
+ runs-on: ubuntu-22.04
+
+ env:
+ HOST: ${{ matrix.host }}
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Linux setup
+ run: |
+ sudo dpkg --add-architecture i386
+ sudo apt-get update
+ sudo apt-get install \
+ gcc-mingw-w64 \
+ autoconf \
+ automake \
+ autotools-dev \
+ libtool \
+ pkg-config \
+ wine
+ - name: Build CUnit
+ run: |
+ curl -LO https://jaist.dl.sourceforge.net/project/cunit/CUnit/2.1-3/CUnit-2.1-3.tar.bz2
+ tar xf CUnit-2.1-3.tar.bz2
+ cd CUnit-2.1-3
+ ./bootstrap
+ ./configure --disable-shared --host="$HOST" --prefix="$PWD/build"
+ make -j$(nproc) install
+ - name: Configure autotools
+ run: |
+ autoreconf -i && \
+ ./configure --enable-werror --enable-lib-only --with-cunit \
+ --host="$HOST" PKG_CONFIG_PATH="$PWD/CUnit-2.1-3/build/lib/pkgconfig"
+ - name: Build nghttp3
+ run: |
+ make -j$(nproc)
+ make -j$(nproc) check TESTS=""
+ - name: Run tests
+ if: matrix.host == 'x86_64-w64-mingw32'
+ run: |
+ cd tests
+ wine main.exe
+
+ build-windows:
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Configure cmake
+ run: |
+ mkdir build
+ cd build
+ cmake -DENABLE_LIB_ONLY=ON ..
+ - name: Build nghttp3
+ run: |
+ cmake --build build
diff --git a/.github/workflows/cflite_batch.yaml b/.github/workflows/cflite_batch.yaml
new file mode 100644
index 0000000..781b83e
--- /dev/null
+++ b/.github/workflows/cflite_batch.yaml
@@ -0,0 +1,29 @@
+name: ClusterFuzzLite batch fuzzing
+on:
+ schedule:
+ - cron: '0 0/6 * * *'
+permissions: read-all
+jobs:
+ BatchFuzzing:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ sanitizer:
+ - address
+ - undefined
+ - memory
+ steps:
+ - name: Build Fuzzers (${{ matrix.sanitizer }})
+ id: build
+ uses: google/clusterfuzzlite/actions/build_fuzzers@v1
+ with:
+ sanitizer: ${{ matrix.sanitizer }}
+ - name: Run Fuzzers (${{ matrix.sanitizer }})
+ id: run
+ uses: google/clusterfuzzlite/actions/run_fuzzers@v1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ fuzz-seconds: 3600
+ mode: 'batch'
+ sanitizer: ${{ matrix.sanitizer }}
diff --git a/.github/workflows/cflite_cron.yaml b/.github/workflows/cflite_cron.yaml
new file mode 100644
index 0000000..d46a4da
--- /dev/null
+++ b/.github/workflows/cflite_cron.yaml
@@ -0,0 +1,19 @@
+name: ClusterFuzzLite cron tasks
+on:
+ schedule:
+ - cron: '0 0 * * *'
+permissions: read-all
+jobs:
+ Pruning:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build Fuzzers
+ id: build
+ uses: google/clusterfuzzlite/actions/build_fuzzers@v1
+ - name: Run Fuzzers
+ id: run
+ uses: google/clusterfuzzlite/actions/run_fuzzers@v1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ fuzz-seconds: 600
+ mode: 'prune'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..81b3ee6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+# emacs backup file
+*~
+
+# autotools
+*.la
+*.lo
+*.m4
+*.o
+*.pyc
+.deps/
+.libs/
+INSTALL
+Makefile
+Makefile.in
+autom4te.cache/
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+test-driver
+
+# test logs generated by `make check`
+*.log
+*.trs
+
+# autotools derivatives
+/lib/includes/nghttp3/version.h
+/lib/libnghttp3.pc
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..4dac20e
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,17 @@
+Alexis La Goutte
+Amir Livneh
+Bryan Call
+Daniel Bevenius
+Daniel Stenberg
+Dimitris Apostolou
+Don
+James M Snell
+Javier Blazquez
+Li Xinwei
+Ondřej Koláček
+Peter Wu
+Tatsuhiro Tsujikawa
+Tim Gates
+Toni Uhlig
+Valère Plantevin
+Viktor Szakats
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..0bd6c81
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,279 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2016 ngtcp2 contributors
+# Copyright (c) 2012 nghttp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+cmake_minimum_required(VERSION 3.1)
+# XXX using 0.1.90 instead of 0.2.0-DEV
+project(nghttp3 VERSION 0.8.0)
+
+# See versioning rule:
+# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+set(LT_CURRENT 3)
+set(LT_REVISION 2)
+set(LT_AGE 0)
+
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+include(Version)
+
+math(EXPR LT_SOVERSION "${LT_CURRENT} - ${LT_AGE}")
+set(LT_VERSION "${LT_SOVERSION}.${LT_AGE}.${LT_REVISION}")
+set(PACKAGE_VERSION "${PROJECT_VERSION}")
+HexVersion(PACKAGE_VERSION_NUM ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR} ${PROJECT_VERSION_PATCH})
+
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the build type" FORCE)
+
+ # Include "None" as option to disable any additional (optimization) flags,
+ # relying on just CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (which are empty by
+ # default). These strings are presented in cmake-gui.
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "None" "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+endif()
+
+include(GNUInstallDirs)
+
+include(CMakeOptions.txt)
+
+# Do not disable assertions based on CMAKE_BUILD_TYPE.
+foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo")
+ foreach(_lang C CXX)
+ string(TOUPPER "CMAKE_${_lang}_FLAGS_${_build_type}" _var)
+ string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " ${_var} "${${_var}}")
+ endforeach()
+endforeach()
+
+find_package(CUnit 2.1)
+enable_testing()
+set(HAVE_CUNIT ${CUNIT_FOUND})
+if(HAVE_CUNIT)
+ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
+endif()
+
+# Checks for header files.
+include(CheckIncludeFile)
+check_include_file("arpa/inet.h" HAVE_ARPA_INET_H)
+check_include_file("stddef.h" HAVE_STDDEF_H)
+check_include_file("stdint.h" HAVE_STDINT_H)
+check_include_file("stdlib.h" HAVE_STDLIB_H)
+check_include_file("string.h" HAVE_STRING_H)
+check_include_file("unistd.h" HAVE_UNISTD_H)
+check_include_file("sys/endian.h" HAVE_SYS_ENDIAN_H)
+check_include_file("endian.h" HAVE_ENDIAN_H)
+check_include_file("byteswap.h" HAVE_BYTESWAP_H)
+
+include(CheckTypeSize)
+# Checks for typedefs, structures, and compiler characteristics.
+# AC_TYPE_SIZE_T
+check_type_size("ssize_t" SIZEOF_SSIZE_T)
+if(SIZEOF_SSIZE_T STREQUAL "")
+ # ssize_t is a signed type in POSIX storing at least -1.
+ # Set it to a pointer-size int.
+ set(ssize_t ptrdiff_t)
+endif()
+
+# Checks for symbols.
+include(CheckSymbolExists)
+if(HAVE_ENDIAN_H)
+ check_symbol_exists(be64toh "endian.h" HAVE_BE64TOH)
+endif()
+if(HAVE_SYS_ENDIAN_H)
+ check_symbol_exists(be64toh "sys/endian.h" HAVE_BE64TOH)
+endif()
+
+check_symbol_exists(bswap_64 "byteswap.h" HAVE_BSWAP_64)
+
+include(ExtractValidFlags)
+set(WARNCFLAGS)
+set(WARNCXXFLAGS)
+if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
+ if(ENABLE_WERROR)
+ set(WARNCFLAGS /WX)
+ set(WARNCXXFLAGS /WX)
+ endif()
+else()
+ if(ENABLE_WERROR)
+ extract_valid_c_flags(WARNCFLAGS -Werror)
+ extract_valid_c_flags(WARNCXXFLAGS -Werror)
+ endif()
+
+ # For C compiler
+ extract_valid_c_flags(WARNCFLAGS
+ -Wall
+ -Wextra
+ -Wmissing-prototypes
+ -Wstrict-prototypes
+ -Wmissing-declarations
+ -Wpointer-arith
+ -Wdeclaration-after-statement
+ -Wformat-security
+ -Wwrite-strings
+ -Wshadow
+ -Winline
+ -Wnested-externs
+ -Wfloat-equal
+ -Wundef
+ -Wendif-labels
+ -Wempty-body
+ -Wcast-align
+ -Wclobbered
+ -Wvla
+ -Wpragmas
+ -Wunreachable-code
+ -Waddress
+ -Wattributes
+ -Wdiv-by-zero
+ -Wshorten-64-to-32
+
+ -Wconversion
+ -Wextended-offsetof
+ -Wformat-nonliteral
+ -Wlanguage-extension-token
+ -Wmissing-field-initializers
+ -Wmissing-noreturn
+ -Wmissing-variable-declarations
+ # Not used because we cannot change public structs
+ # -Wpadded
+ -Wsign-conversion
+ # Not used because this basically disallows default case
+ # -Wswitch-enum
+ -Wunreachable-code-break
+ -Wunused-macros
+ -Wunused-parameter
+ -Wredundant-decls
+ # Only work with Clang for the moment
+ -Wheader-guard
+ -Wsometimes-uninitialized
+
+ # Only work with gcc7 for the moment
+ -Wduplicated-branches
+ # This is required because we pass format string as "const char*.
+ -Wno-format-nonliteral
+ )
+
+ extract_valid_cxx_flags(WARNCXXFLAGS
+ # For C++ compiler
+ -Wall
+ -Wformat-security
+ -Wsometimes-uninitialized
+ # Disable noexcept-type warning of g++-7. This is not harmful as
+ # long as all source files are compiled with the same compiler.
+ -Wno-noexcept-type
+ )
+
+ if(ENABLE_ASAN)
+ include(CMakePushCheckState)
+ cmake_push_check_state()
+ set(CMAKE_REQUIRED_LIBRARIES "-fsanitize=address")
+ check_c_compiler_flag(-fsanitize=address C__fsanitize_address_VALID)
+ check_cxx_compiler_flag(-fsanitize=address CXX__fsanitize_address_VALID)
+ cmake_pop_check_state()
+ if(NOT C__fsanitize_address_VALID OR NOT CXX__fsanitize_address_VALID)
+ message(WARNING "ENABLE_ASAN was requested, but not supported!")
+ else()
+ set(CMAKE_C_FLAGS "-fsanitize=address ${CMAKE_C_FLAGS}")
+ set(CMAKE_CXX_FLAGS "-fsanitize=address ${CMAKE_CXX_FLAGS}")
+ endif()
+ endif()
+endif()
+
+if(ENABLE_STATIC_CRT)
+ foreach(lang C CXX)
+ foreach(suffix "" _DEBUG _MINSIZEREL _RELEASE _RELWITHDEBINFO)
+ set(var "CMAKE_${lang}_FLAGS${suffix}")
+ string(REPLACE "/MD" "/MT" ${var} "${${var}}")
+ endforeach()
+ endforeach()
+endif()
+
+if(ENABLE_DEBUG)
+ set(DEBUGBUILD 1)
+endif()
+
+if(ENABLE_LIB_ONLY)
+ set(ENABLE_EXAMPLES 0)
+else()
+ set(ENABLE_EXAMPLES 1)
+endif()
+
+add_definitions(-DHAVE_CONFIG_H)
+configure_file(cmakeconfig.h.in config.h)
+# autotools-compatible names
+# Sphinx expects relative paths in the .rst files. Use the fact that the files
+# below are all one directory level deep.
+file(RELATIVE_PATH top_srcdir "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_SOURCE_DIR}")
+file(RELATIVE_PATH top_builddir "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_BINARY_DIR}")
+set(abs_top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}")
+set(abs_top_builddir "${CMAKE_CURRENT_BINARY_DIR}")
+# libnghttp3.pc (pkg-config file)
+set(prefix "${CMAKE_INSTALL_PREFIX}")
+set(exec_prefix "${CMAKE_INSTALL_PREFIX}")
+set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
+set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
+set(VERSION "${PACKAGE_VERSION}")
+# For init scripts and systemd service file (in contrib/)
+set(bindir "${CMAKE_INSTALL_FULL_BINDIR}")
+set(sbindir "${CMAKE_INSTALL_FULL_SBINDIR}")
+foreach(name
+ lib/libnghttp3.pc
+ lib/includes/nghttp3/version.h
+)
+ configure_file("${name}.in" "${name}" @ONLY)
+endforeach()
+
+include_directories(
+ "${CMAKE_CURRENT_BINARY_DIR}" # for config.h
+)
+# For use in src/CMakeLists.txt
+set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${CMAKE_PROJECT_NAME}")
+
+install(FILES README.rst DESTINATION "${CMAKE_INSTALL_DOCDIR}")
+
+add_subdirectory(lib)
+add_subdirectory(tests)
+add_subdirectory(examples)
+
+
+string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
+message(STATUS "summary of build options:
+
+ Package version: ${VERSION}
+ Library version: ${LT_CURRENT}:${LT_REVISION}:${LT_AGE}
+ Install prefix: ${CMAKE_INSTALL_PREFIX}
+ Target system: ${CMAKE_SYSTEM_NAME}
+ Compiler:
+ Build type: ${CMAKE_BUILD_TYPE}
+ C compiler: ${CMAKE_C_COMPILER}
+ CFLAGS: ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS}
+ C++ compiler: ${CMAKE_CXX_COMPILER}
+ CXXFLAGS: ${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS}
+ WARNCFLAGS: ${WARNCFLAGS}
+ WARNCXXFLAGS: ${WARNCXXFLAGS}
+ Library:
+ Shared: ${ENABLE_SHARED_LIB}
+ Static: ${ENABLE_STATIC_LIB}
+ Test:
+ CUnit: ${HAVE_CUNIT} (LIBS='${CUNIT_LIBRARIES}')
+ Library only: ${ENABLE_LIB_ONLY}
+ Examples: ${ENABLE_EXAMPLES}
+")
diff --git a/CMakeOptions.txt b/CMakeOptions.txt
new file mode 100644
index 0000000..7f0a2e1
--- /dev/null
+++ b/CMakeOptions.txt
@@ -0,0 +1,11 @@
+# Features that can be enabled for cmake (see CMakeLists.txt)
+
+option(ENABLE_WERROR "Make compiler warnings fatal" OFF)
+option(ENABLE_DEBUG "Turn on debug output")
+option(ENABLE_ASAN "Enable AddressSanitizer (ASAN)" OFF)
+option(ENABLE_LIB_ONLY "Build libnghttp3 only" OFF)
+option(ENABLE_STATIC_LIB "Build libnghttp3 as a static library" ON)
+option(ENABLE_SHARED_LIB "Build libnghttp3 as a shared library" ON)
+option(ENABLE_STATIC_CRT "Build libnghttp3 against the MS LIBCMT[d]")
+
+# vim: ft=cmake:
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..37562ea
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2019 nghttp3 contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..ce15bcb
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,45 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2016 ngtcp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+SUBDIRS = lib tests doc examples
+
+ACLOCAL_AMFLAGS = -I m4
+
+dist_doc_DATA = README.rst
+
+EXTRA_DIST = \
+ cmakeconfig.h.in \
+ CMakeLists.txt \
+ CMakeOptions.txt \
+ cmake/ExtractValidFlags.cmake \
+ cmake/FindCUnit.cmake \
+ cmake/Version.cmake
+
+# Format source files using clang-format. Don't format source files
+# under third-party directory since we are not responsible for their
+# coding style.
+clang-format:
+ CLANGFORMAT=`git config --get clangformat.binary`; \
+ test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
+ $${CLANGFORMAT} -i lib/*.{c,h} tests/*.{c,h} lib/includes/nghttp3/*.h \
+ examples/*.{cc,h}
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..5ccc0ea
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+See README.rst
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..7170da6
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,42 @@
+nghttp3
+=======
+
+nghttp3 is an implementation of `RFC 9114
+<https://datatracker.ietf.org/doc/html/rfc9114>`_ HTTP/3 mapping over
+QUIC and `RFC 9204 <https://datatracker.ietf.org/doc/html/rfc9204>`_
+QPACK in C.
+
+It does not depend on any particular QUIC transport implementation.
+
+Documentation
+-------------
+
+`Online documentation <https://nghttp2.org/nghttp3/>`_ is available.
+
+HTTP/3
+------
+
+This library implements `RFC 9114
+<https://datatracker.ietf.org/doc/html/rfc9114>`_ HTTP/3. It does not
+support server push.
+
+The following extensions have been implemented:
+
+- `Extensible Prioritization Scheme for HTTP
+ <https://datatracker.ietf.org/doc/html/rfc9218>`_
+- `Bootstrapping WebSockets with HTTP/3
+ <https://datatracker.ietf.org/doc/html/rfc9220>`_
+
+QPACK
+-----
+
+This library implements `RFC 9204
+<https://datatracker.ietf.org/doc/html/rfc9204>`_ QPACK. It supports
+dynamic table.
+
+License
+-------
+
+The MIT License
+
+Copyright (c) 2019 nghttp3 contributors
diff --git a/cmake/ExtractValidFlags.cmake b/cmake/ExtractValidFlags.cmake
new file mode 100644
index 0000000..ccd57dc
--- /dev/null
+++ b/cmake/ExtractValidFlags.cmake
@@ -0,0 +1,31 @@
+# Convenience function that checks the availability of certain
+# C or C++ compiler flags and returns valid ones as a string.
+
+include(CheckCCompilerFlag)
+include(CheckCXXCompilerFlag)
+
+function(extract_valid_c_flags varname)
+ set(valid_flags)
+ foreach(flag IN LISTS ARGN)
+ string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag})
+ set(flag_var "C_FLAG_${flag_var}")
+ check_c_compiler_flag("${flag}" "${flag_var}")
+ if(${flag_var})
+ set(valid_flags "${valid_flags} ${flag}")
+ endif()
+ endforeach()
+ set(${varname} "${valid_flags}" PARENT_SCOPE)
+endfunction()
+
+function(extract_valid_cxx_flags varname)
+ set(valid_flags)
+ foreach(flag IN LISTS ARGN)
+ string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag})
+ set(flag_var "CXX_FLAG_${flag_var}")
+ check_cxx_compiler_flag("${flag}" "${flag_var}")
+ if(${flag_var})
+ set(valid_flags "${valid_flags} ${flag}")
+ endif()
+ endforeach()
+ set(${varname} "${valid_flags}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/FindCUnit.cmake b/cmake/FindCUnit.cmake
new file mode 100644
index 0000000..ada87c1
--- /dev/null
+++ b/cmake/FindCUnit.cmake
@@ -0,0 +1,40 @@
+# - Try to find cunit
+# Once done this will define
+# CUNIT_FOUND - System has cunit
+# CUNIT_INCLUDE_DIRS - The cunit include directories
+# CUNIT_LIBRARIES - The libraries needed to use cunit
+
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_CUNIT QUIET cunit)
+
+find_path(CUNIT_INCLUDE_DIR
+ NAMES CUnit/CUnit.h
+ HINTS ${PC_CUNIT_INCLUDE_DIRS}
+)
+find_library(CUNIT_LIBRARY
+ NAMES cunit
+ HINTS ${PC_CUNIT_LIBRARY_DIRS}
+)
+
+if(CUNIT_INCLUDE_DIR)
+ set(_version_regex "^#define[ \t]+CU_VERSION[ \t]+\"([^\"]+)\".*")
+ file(STRINGS "${CUNIT_INCLUDE_DIR}/CUnit/CUnit.h"
+ CUNIT_VERSION REGEX "${_version_regex}")
+ string(REGEX REPLACE "${_version_regex}" "\\1"
+ CUNIT_VERSION "${CUNIT_VERSION}")
+ unset(_version_regex)
+endif()
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set CUNIT_FOUND to TRUE
+# if all listed variables are TRUE and the requested version matches.
+find_package_handle_standard_args(CUnit REQUIRED_VARS
+ CUNIT_LIBRARY CUNIT_INCLUDE_DIR
+ VERSION_VAR CUNIT_VERSION)
+
+if(CUNIT_FOUND)
+ set(CUNIT_LIBRARIES ${CUNIT_LIBRARY})
+ set(CUNIT_INCLUDE_DIRS ${CUNIT_INCLUDE_DIR})
+endif()
+
+mark_as_advanced(CUNIT_INCLUDE_DIR CUNIT_LIBRARY)
diff --git a/cmake/Version.cmake b/cmake/Version.cmake
new file mode 100644
index 0000000..8ac4849
--- /dev/null
+++ b/cmake/Version.cmake
@@ -0,0 +1,11 @@
+# Converts a version such as 1.2.255 to 0x0102ff
+function(HexVersion version_hex_var major minor patch)
+ math(EXPR version_dec "${major} * 256 * 256 + ${minor} * 256 + ${patch}")
+ set(version_hex "0x")
+ foreach(i RANGE 5 0 -1)
+ math(EXPR num "(${version_dec} >> (4 * ${i})) & 15")
+ string(SUBSTRING "0123456789abcdef" ${num} 1 num_hex)
+ set(version_hex "${version_hex}${num_hex}")
+ endforeach()
+ set(${version_hex_var} "${version_hex}" PARENT_SCOPE)
+endfunction()
diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in
new file mode 100644
index 0000000..41fbb0b
--- /dev/null
+++ b/cmakeconfig.h.in
@@ -0,0 +1,33 @@
+
+/* Define to `int' if <sys/types.h> does not define. */
+#cmakedefine ssize_t @ssize_t@
+
+/* Define to 1 to enable debug output. */
+#cmakedefine DEBUGBUILD 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#cmakedefine HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#cmakedefine HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#cmakedefine HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#cmakedefine HAVE_STRING_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#cmakedefine HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+#cmakedefine HAVE_SYS_ENDIAN_H 1
+
+/* Define to 1 if you have the <endian.h> header file. */
+#cmakedefine HAVE_ENDIAN_H 1
+
+/* Define to 1 if you have the `be64toh' function. */
+#cmakedefine HAVE_BE64TOH 1
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a4ff7ad
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,353 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2016 ngtcp2 contributors
+# Copyright (c) 2012 nghttp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+AC_PREREQ(2.61)
+AC_INIT([nghttp3], [0.8.0], [t-tujikawa@users.sourceforge.net])
+AC_CONFIG_AUX_DIR([.])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
+AC_USE_SYSTEM_EXTENSIONS
+
+LT_PREREQ([2.2.6])
+LT_INIT()
+
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+AC_CANONICAL_TARGET
+
+AM_INIT_AUTOMAKE([subdir-objects])
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+# See versioning rule:
+# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+AC_SUBST(LT_CURRENT, 3)
+AC_SUBST(LT_REVISION, 2)
+AC_SUBST(LT_AGE, 0)
+
+# from nghttp2
+major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
+minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
+patch=`echo $PACKAGE_VERSION |cut -d. -f3 | cut -d- -f1 | sed -e "s/[^0-9]//g"`
+
+PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
+
+AC_SUBST(PACKAGE_VERSION_NUM)
+
+# Checks for command-line options
+AC_ARG_ENABLE([werror],
+ [AS_HELP_STRING([--enable-werror],
+ [Turn on compile time warnings])],
+ [werror=$enableval], [werror=no])
+
+AC_ARG_ENABLE([debug],
+ [AS_HELP_STRING([--enable-debug],
+ [Turn on debug output])],
+ [debug=$enableval], [debug=no])
+
+if test "x${debug}" = "xyes"; then
+ DEBUGCFLAGS="-O0 -g3"
+ AC_SUBST([DEBUGCFLAGS])
+ AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
+fi
+
+AC_ARG_ENABLE([memdebug],
+ [AS_HELP_STRING([--enable-memdebug],
+ [Turn on memory allocation debug output])],
+ [memdebug=$enableval], [memdebug=no])
+
+AC_ARG_ENABLE(asan,
+ AS_HELP_STRING([--enable-asan],
+ [Enable AddressSanitizer (ASAN)]),
+ [asan=$enableval], [asan=no])
+
+AC_ARG_ENABLE([lib-only],
+ [AS_HELP_STRING([--enable-lib-only],
+ [Build libnghttp3 only.])],
+ [lib_only=$enableval], [lib_only=no])
+
+AC_ARG_WITH([cunit],
+ [AS_HELP_STRING([--with-cunit],
+ [Use cunit [default=check]])],
+ [request_cunit=$withval], [request_cunit=check])
+
+# Checks for programs
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_MKDIR_P
+
+PKG_PROG_PKG_CONFIG([0.20])
+
+AX_CXX_COMPILE_STDCXX([17], [noext], [optional])
+
+# Checks for libraries.
+
+# cunit
+have_cunit=no
+if test "x${request_cunit}" != "xno"; then
+ PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])
+ # If pkg-config does not find cunit, check it using AC_CHECK_LIB. We
+ # do this because Debian (Ubuntu) lacks pkg-config file for cunit.
+ if test "x${have_cunit}" = "xno"; then
+ AC_MSG_WARN([${CUNIT_PKG_ERRORS}])
+ AC_CHECK_LIB([cunit], [CU_initialize_registry],
+ [have_cunit=yes], [have_cunit=no])
+ if test "x${have_cunit}" = "xyes"; then
+ CUNIT_LIBS="-lcunit"
+ CUNIT_CFLAGS=""
+ AC_SUBST([CUNIT_LIBS])
+ AC_SUBST([CUNIT_CFLAGS])
+ fi
+ fi
+ if test "x${have_cunit}" = "xyes"; then
+ # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test
+ # program can be built without -lncurses, but it emits runtime
+ # error.
+ case "${build}" in
+ *-apple-darwin*)
+ CUNIT_LIBS="$CUNIT_LIBS -lncurses"
+ AC_SUBST([CUNIT_LIBS])
+ ;;
+ esac
+ fi
+fi
+
+if test "x${request_cunit}" = "xyes" &&
+ test "x${have_cunit}" != "xyes"; then
+ AC_MSG_ERROR([cunit was requested (--with-cunit) but not found])
+fi
+
+AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
+
+enable_examples=yes
+if test "x${lib_only}" = "xyes"; then
+ enable_examples=no
+fi
+
+AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ])
+
+# Checks for header files.
+AC_CHECK_HEADERS([ \
+ arpa/inet.h \
+ stddef.h \
+ stdint.h \
+ stdlib.h \
+ string.h \
+ unistd.h \
+ sys/endian.h \
+ endian.h \
+ byteswap.h \
+])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_UID_T
+AC_CHECK_TYPES([ptrdiff_t])
+AC_C_BIGENDIAN
+AC_C_INLINE
+AC_SYS_LARGEFILE
+
+# Checks for library functions.
+AC_CHECK_FUNCS([ \
+ memmove \
+ memset \
+])
+
+# Checks for symbols.
+AC_CHECK_DECLS([be64toh], [], [], [[
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif
+]])
+
+AC_CHECK_DECLS([bswap_64], [], [], [[
+#include <byteswap.h>
+]])
+
+# More compiler flags from nghttp2.
+save_CFLAGS=$CFLAGS
+save_CXXFLAGS=$CXXFLAGS
+
+CFLAGS=
+CXXFLAGS=
+
+if test "x$werror" != "xno"; then
+ # For C compiler
+ AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
+ AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
+ AX_CHECK_COMPILE_FLAG([-Werror], [CFLAGS="$CFLAGS -Werror"])
+ AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CFLAGS="$CFLAGS -Wmissing-prototypes"])
+ AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CFLAGS="$CFLAGS -Wstrict-prototypes"])
+ AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CFLAGS="$CFLAGS -Wmissing-declarations"])
+ AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CFLAGS="$CFLAGS -Wpointer-arith"])
+ AX_CHECK_COMPILE_FLAG([-Wdeclaration-after-statement], [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
+ AX_CHECK_COMPILE_FLAG([-Wformat-security], [CFLAGS="$CFLAGS -Wformat-security"])
+ AX_CHECK_COMPILE_FLAG([-Wwrite-strings], [CFLAGS="$CFLAGS -Wwrite-strings"])
+ AX_CHECK_COMPILE_FLAG([-Wshadow], [CFLAGS="$CFLAGS -Wshadow"])
+ AX_CHECK_COMPILE_FLAG([-Winline], [CFLAGS="$CFLAGS -Winline"])
+ AX_CHECK_COMPILE_FLAG([-Wnested-externs], [CFLAGS="$CFLAGS -Wnested-externs"])
+ AX_CHECK_COMPILE_FLAG([-Wfloat-equal], [CFLAGS="$CFLAGS -Wfloat-equal"])
+ AX_CHECK_COMPILE_FLAG([-Wundef], [CFLAGS="$CFLAGS -Wundef"])
+ AX_CHECK_COMPILE_FLAG([-Wendif-labels], [CFLAGS="$CFLAGS -Wendif-labels"])
+ AX_CHECK_COMPILE_FLAG([-Wempty-body], [CFLAGS="$CFLAGS -Wempty-body"])
+ AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
+ AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
+ AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
+ AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
+ AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
+ AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
+ AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS="$CFLAGS -Wattributes"])
+ AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"])
+ AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS="$CFLAGS -Wshorten-64-to-32"])
+
+ AX_CHECK_COMPILE_FLAG([-Wconversion], [CFLAGS="$CFLAGS -Wconversion"])
+ AX_CHECK_COMPILE_FLAG([-Wextended-offsetof], [CFLAGS="$CFLAGS -Wextended-offsetof"])
+ AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral], [CFLAGS="$CFLAGS -Wformat-nonliteral"])
+ AX_CHECK_COMPILE_FLAG([-Wlanguage-extension-token], [CFLAGS="$CFLAGS -Wlanguage-extension-token"])
+ AX_CHECK_COMPILE_FLAG([-Wmissing-field-initializers], [CFLAGS="$CFLAGS -Wmissing-field-initializers"])
+ AX_CHECK_COMPILE_FLAG([-Wmissing-noreturn], [CFLAGS="$CFLAGS -Wmissing-noreturn"])
+ AX_CHECK_COMPILE_FLAG([-Wmissing-variable-declarations], [CFLAGS="$CFLAGS -Wmissing-variable-declarations"])
+ # Not used because we cannot change public structs
+ # AX_CHECK_COMPILE_FLAG([-Wpadded], [CFLAGS="$CFLAGS -Wpadded"])
+ AX_CHECK_COMPILE_FLAG([-Wsign-conversion], [CFLAGS="$CFLAGS -Wsign-conversion"])
+ # Not used because this basically disallows default case
+ # AX_CHECK_COMPILE_FLAG([-Wswitch-enum], [CFLAGS="$CFLAGS -Wswitch-enum"])
+ AX_CHECK_COMPILE_FLAG([-Wunreachable-code-break], [CFLAGS="$CFLAGS -Wunreachable-code-break"])
+ AX_CHECK_COMPILE_FLAG([-Wunused-macros], [CFLAGS="$CFLAGS -Wunused-macros"])
+ AX_CHECK_COMPILE_FLAG([-Wunused-parameter], [CFLAGS="$CFLAGS -Wunused-parameter"])
+ AX_CHECK_COMPILE_FLAG([-Wredundant-decls], [CFLAGS="$CFLAGS -Wredundant-decls"])
+ # Only work with Clang for the moment
+ AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
+ AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CFLAGS="$CFLAGS -Wsometimes-uninitialized"])
+
+ # Only work with gcc7 for the moment
+ AX_CHECK_COMPILE_FLAG([-Wduplicated-branches], [CFLAGS="$CFLAGS -Wduplicated-branches"])
+
+ # This is required because we pass format string as "const char*.
+ AX_CHECK_COMPILE_FLAG([-Wno-format-nonliteral], [CFLAGS="$CFLAGS -Wno-format-nonliteral"])
+
+ # For C++ compiler
+ AC_LANG_PUSH(C++)
+ AX_CHECK_COMPILE_FLAG([-Wall], [CXXFLAGS="$CXXFLAGS -Wall"])
+ # TODO separate option for -Werror and warnings?
+ #AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAGS="$CXXFLAGS -Werror"])
+ AX_CHECK_COMPILE_FLAG([-Wformat-security], [CXXFLAGS="$CXXFLAGS -Wformat-security"])
+ AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized"])
+ # Disable noexcept-type warning of g++-7. This is not harmful as
+ # long as all source files are compiled with the same compiler.
+ AX_CHECK_COMPILE_FLAG([-Wno-noexcept-type], [CXXFLAGS="$CXXFLAGS -Wno-noexcept-type"])
+ AC_LANG_POP()
+fi
+
+WARNCFLAGS=$CFLAGS
+WARNCXXFLAGS=$CXXFLAGS
+
+CFLAGS=$save_CFLAGS
+CXXFLAGS=$save_CXXFLAGS
+
+AC_SUBST([WARNCFLAGS])
+AC_SUBST([WARNCXXFLAGS])
+
+if test "x$asan" != "xno"; then
+ # Assume both C and C++ compiler either support ASAN or not.
+ LDFLAGS_saved="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -fsanitize=address"
+ AX_CHECK_COMPILE_FLAG([-fsanitize=address],
+ [CFLAGS="$CFLAGS -fsanitize=address"; CXXFLAGS="$CXXFLAGS -fsanitize=address"],
+ [LDFLAGS="$LDFLAGS_saved"])
+fi
+
+if test "x${memdebug}" = "xyes"; then
+ AC_DEFINE([MEMDEBUG], [1],
+ [Define to 1 to enable memory allocation debug output.])
+fi
+
+# extra flags for API function visibility
+EXTRACFLAG=
+AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [EXTRACFLAG="-fvisibility=hidden"])
+
+AC_SUBST([EXTRACFLAG])
+
+AC_CONFIG_FILES([
+ Makefile
+ lib/Makefile
+ lib/libnghttp3.pc
+ lib/includes/Makefile
+ lib/includes/nghttp3/version.h
+ tests/Makefile
+ doc/Makefile
+ doc/source/conf.py
+ examples/Makefile
+])
+AC_OUTPUT
+
+AC_MSG_NOTICE([summary of build options:
+
+ Package version: ${VERSION}
+ Library version: $LT_CURRENT:$LT_REVISION:$LT_AGE
+ Install prefix: ${prefix}
+ System types:
+ Build: ${build}
+ Host: ${host}
+ Target: ${target}
+ Compiler:
+ C preprocessor: ${CPP}
+ CPPFLAGS: ${CPPFLAGS}
+ C compiler: ${CC}
+ CFLAGS: ${CFLAGS}
+ C++ compiler: ${CXX}
+ CXXFLAGS: ${CXXFLAGS}
+ LDFLAGS: ${LDFLAGS}
+ WARNCFLAGS: ${WARNCFLAGS}
+ WARNCXXFLAGS: ${WARNCXXFLAGS}
+ EXTRACFLAG: ${EXTRACFLAG}
+ LIBS: ${LIBS}
+ Library:
+ Shared: ${enable_shared}
+ Static: ${enable_static}
+ Test:
+ CUnit: ${have_cunit} (CFLAGS='${CUNIT_CFLAGS}' LIBS='${CUNIT_LIBS}')
+ Debug:
+ Debug: ${debug} (CFLAGS='${DEBUGCFLAGS}')
+ Library only: ${lib_only}
+ Examples: ${enable_examples}
+])
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..6ede162
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,51 @@
+# nghttp3
+
+# Copyright (c) 2020 nghttp3 contributors
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help html apiref
+
+apiref: mkapiref.py \
+ $(top_builddir)/lib/includes/nghttp3/version.h \
+ $(top_srcdir)/lib/includes/nghttp3/nghttp3.h
+ $(top_srcdir)/doc/mkapiref.py \
+ source/apiref.rst source/macros.rst source/enums.rst source/types.rst \
+ source $(wordlist 2, $(words $^), $^)
+
+html-local: apiref
+ @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+clean-local:
+ -rm -rf "$(BUILDDIR)"
diff --git a/doc/make.bat b/doc/make.bat
new file mode 100644
index 0000000..6247f7e
--- /dev/null
+++ b/doc/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/doc/mkapiref.py b/doc/mkapiref.py
new file mode 100755
index 0000000..71337a7
--- /dev/null
+++ b/doc/mkapiref.py
@@ -0,0 +1,358 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# nghttp2 - HTTP/2 C Library
+#
+# Copyright (c) 2020 nghttp3 contributors
+# Copyright (c) 2020 ngtcp2 contributors
+# Copyright (c) 2012 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.
+
+# Generates API reference from C source code.
+
+import re, sys, argparse, os.path
+
+class FunctionDoc:
+ def __init__(self, name, content, domain, filename):
+ self.name = name
+ self.content = content
+ self.domain = domain
+ if self.domain == 'function':
+ self.funcname = re.search(r'(nghttp3_[^ )]+)\(', self.name).group(1)
+ self.filename = filename
+
+ def write(self, out):
+ out.write('.. {}:: {}\n'.format(self.domain, self.name))
+ out.write('\n')
+ for line in self.content:
+ out.write(' {}\n'.format(line))
+
+class StructDoc:
+ def __init__(self, name, content, members, member_domain):
+ self.name = name
+ self.content = content
+ self.members = members
+ self.member_domain = member_domain
+
+ def write(self, out):
+ if self.name:
+ out.write('.. type:: {}\n'.format(self.name))
+ out.write('\n')
+ for line in self.content:
+ out.write(' {}\n'.format(line))
+ out.write('\n')
+ for name, content in self.members:
+ out.write(' .. {}:: {}\n'.format(self.member_domain, name))
+ out.write('\n')
+ for line in content:
+ out.write(' {}\n'.format(line))
+ out.write('\n')
+
+class EnumDoc:
+ def __init__(self, name, content, members):
+ self.name = name
+ self.content = content
+ self.members = members
+
+ def write(self, out):
+ if self.name:
+ out.write('.. type:: {}\n'.format(self.name))
+ out.write('\n')
+ for line in self.content:
+ out.write(' {}\n'.format(line))
+ out.write('\n')
+ for name, content in self.members:
+ out.write(' .. enum:: {}\n'.format(name))
+ out.write('\n')
+ for line in content:
+ out.write(' {}\n'.format(line))
+ out.write('\n')
+
+class MacroDoc:
+ def __init__(self, name, content):
+ self.name = name
+ self.content = content
+
+ def write(self, out):
+ out.write('''.. macro:: {}\n'''.format(self.name))
+ out.write('\n')
+ for line in self.content:
+ out.write(' {}\n'.format(line))
+
+class MacroSectionDoc:
+ def __init__(self, content):
+ self.content = content
+
+ def write(self, out):
+ out.write('\n')
+ c = ' '.join(self.content).strip()
+ out.write(c)
+ out.write('\n')
+ out.write('-' * len(c))
+ out.write('\n\n')
+
+class TypedefDoc:
+ def __init__(self, name, content):
+ self.name = name
+ self.content = content
+
+ def write(self, out):
+ out.write('''.. type:: {}\n'''.format(self.name))
+ out.write('\n')
+ for line in self.content:
+ out.write(' {}\n'.format(line))
+
+def make_api_ref(infile):
+ macros = []
+ enums = []
+ types = []
+ functions = []
+ while True:
+ line = infile.readline()
+ if not line:
+ break
+ elif line == '/**\n':
+ line = infile.readline()
+ doctype = line.split()[1]
+ if doctype == '@function':
+ functions.append(process_function('function', infile))
+ elif doctype == '@functypedef':
+ types.append(process_function('type', infile))
+ elif doctype == '@struct' or doctype == '@union':
+ types.append(process_struct(infile))
+ elif doctype == '@enum':
+ enums.append(process_enum(infile))
+ elif doctype == '@macro':
+ macros.append(process_macro(infile))
+ elif doctype == '@macrosection':
+ macros.append(process_macrosection(infile))
+ elif doctype == '@typedef':
+ types.append(process_typedef(infile))
+ return macros, enums, types, functions
+
+def output(
+ title, indexfile, macrosfile, enumsfile, typesfile, funcsdir,
+ macros, enums, types, functions):
+ indexfile.write('''
+{title}
+{titledecoration}
+
+.. toctree::
+ :maxdepth: 1
+
+ {macros}
+ {enums}
+ {types}
+'''.format(
+ title=title, titledecoration='='*len(title),
+ macros=os.path.splitext(os.path.basename(macrosfile.name))[0],
+ enums=os.path.splitext(os.path.basename(enumsfile.name))[0],
+ types=os.path.splitext(os.path.basename(typesfile.name))[0],
+))
+
+ for doc in functions:
+ indexfile.write(' {}\n'.format(doc.funcname))
+
+ macrosfile.write('''
+Macros
+======
+''')
+ for doc in macros:
+ doc.write(macrosfile)
+
+ enumsfile.write('''
+Enums
+=====
+''')
+ for doc in enums:
+ doc.write(enumsfile)
+
+ typesfile.write('''
+Types (structs, unions and typedefs)
+====================================
+''')
+ for doc in types:
+ doc.write(typesfile)
+
+ for doc in functions:
+ with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f:
+ f.write('''
+{funcname}
+{secul}
+
+Synopsis
+--------
+
+*#include <nghttp3/{filename}>*
+
+'''.format(funcname=doc.funcname, secul='='*len(doc.funcname),
+ filename=doc.filename))
+ doc.write(f)
+
+def process_macro(infile):
+ content = read_content(infile)
+ lines = []
+ while True:
+ line = infile.readline()
+ if not line:
+ break
+ line = line.rstrip()
+ lines.append(line.rstrip('\\'))
+ if not line.endswith('\\'):
+ break
+
+ macro_name = re.sub(r'#define ', '', ''.join(lines))
+ m = re.match(r'^[^( ]+(:?\(.*?\))?', macro_name)
+ macro_name = m.group(0)
+ return MacroDoc(macro_name, content)
+
+def process_macrosection(infile):
+ content = read_content(infile)
+ return MacroSectionDoc(content)
+
+def process_typedef(infile):
+ content = read_content(infile)
+ typedef = infile.readline()
+ typedef = re.sub(r';\n$', '', typedef)
+ typedef = re.sub(r'typedef ', '', typedef)
+ return TypedefDoc(typedef, content)
+
+def process_enum(infile):
+ members = []
+ enum_name = None
+ content = read_content(infile)
+ while True:
+ line = infile.readline()
+ if not line:
+ break
+ elif re.match(r'\s*/\*\*\n', line):
+ member_content = read_content(infile)
+ line = infile.readline()
+ items = line.split()
+ member_name = items[0].rstrip(',')
+ if len(items) >= 3:
+ member_content.insert(0, '(``{}``) '\
+ .format(' '.join(items[2:]).rstrip(',')))
+ members.append((member_name, member_content))
+ elif line.startswith('}'):
+ enum_name = line.rstrip().split()[1]
+ enum_name = re.sub(r';$', '', enum_name)
+ break
+ return EnumDoc(enum_name, content, members)
+
+def process_struct(infile):
+ members = []
+ struct_name = None
+ content = read_content(infile)
+ while True:
+ line = infile.readline()
+ if not line:
+ break
+ elif re.match(r'\s*/\*\*\n', line):
+ member_content = read_content(infile)
+ line = infile.readline()
+ member_name = line.rstrip().rstrip(';')
+ members.append((member_name, member_content))
+ elif line.startswith('}') or\
+ (line.startswith('typedef ') and line.endswith(';\n')):
+ if line.startswith('}'):
+ index = 1
+ else:
+ index = 3
+ struct_name = line.rstrip().split()[index]
+ struct_name = re.sub(r';$', '', struct_name)
+ break
+ return StructDoc(struct_name, content, members, 'member')
+
+def process_function(domain, infile):
+ content = read_content(infile)
+ func_proto = []
+ while True:
+ line = infile.readline()
+ if not line:
+ break
+ elif line == '\n':
+ break
+ else:
+ func_proto.append(line)
+ func_proto = ''.join(func_proto)
+ func_proto = re.sub(r'int (settings|callbacks)_version,',
+ '', func_proto)
+ func_proto = re.sub(r'_versioned\(', '(', func_proto)
+ func_proto = re.sub(r';\n$', '', func_proto)
+ func_proto = re.sub(r'\s+', ' ', func_proto)
+ func_proto = re.sub(r'NGHTTP3_EXTERN ', '', func_proto)
+ func_proto = re.sub(r'typedef ', '', func_proto)
+ filename = os.path.basename(infile.name)
+ return FunctionDoc(func_proto, content, domain, filename)
+
+def read_content(infile):
+ content = []
+ while True:
+ line = infile.readline()
+ if not line:
+ break
+ if re.match(r'\s*\*/\n', line):
+ break
+ else:
+ content.append(transform_content(line.rstrip()))
+ return content
+
+def arg_repl(matchobj):
+ return '*{}*'.format(matchobj.group(1).replace('*', '\\*'))
+
+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__':
+ parser = argparse.ArgumentParser(description="Generate API reference")
+ parser.add_argument('--title', default='API Reference',
+ help='title of index page')
+ parser.add_argument('index', type=argparse.FileType('w'),
+ help='index output file')
+ parser.add_argument('macros', type=argparse.FileType('w'),
+ help='macros section output file. The filename should be macros.rst')
+ parser.add_argument('enums', type=argparse.FileType('w'),
+ help='enums section output file. The filename should be enums.rst')
+ parser.add_argument('types', type=argparse.FileType('w'),
+ help='types section output file. The filename should be types.rst')
+ parser.add_argument('funcsdir',
+ help='functions doc output dir')
+ parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
+ help='source file')
+ args = parser.parse_args()
+ macros = []
+ enums = []
+ types = []
+ funcs = []
+ for infile in args.files:
+ m, e, t, f = make_api_ref(infile)
+ macros.extend(m)
+ enums.extend(e)
+ types.extend(t)
+ funcs.extend(f)
+ funcs.sort(key=lambda x: x.funcname)
+ output(
+ args.title,
+ args.index, args.macros, args.enums, args.types, args.funcsdir,
+ macros, enums, types, funcs)
diff --git a/doc/source/.gitignore b/doc/source/.gitignore
new file mode 100644
index 0000000..a1a2e88
--- /dev/null
+++ b/doc/source/.gitignore
@@ -0,0 +1,5 @@
+/conf.py
+/apiref.rst
+/enums.rst
+/macros.rst
+/types.rst
diff --git a/doc/source/conf.py.in b/doc/source/conf.py.in
new file mode 100644
index 0000000..171595f
--- /dev/null
+++ b/doc/source/conf.py.in
@@ -0,0 +1,94 @@
+# nghttp3
+
+# Copyright (c) 2020 nghttp3 contributors
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'nghttp3'
+copyright = '2020, nghttp3 contributors'
+author = 'nghttp3 contributors'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'alabaster'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+default_role = 'c:func'
+primary_domain = 'c'
+
+# manpage URL pattern
+manpages_url = 'https://man7.org/linux/man-pages/man{section}/{page}.{section}.html'
+
+# The default language to highlight source code in.
+highlight_language = 'c'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '@PACKAGE_VERSION@'
+# The full version, including alpha/beta/rc tags.
+release = '@PACKAGE_VERSION@'
diff --git a/doc/source/index.rst b/doc/source/index.rst
new file mode 100644
index 0000000..2ba7331
--- /dev/null
+++ b/doc/source/index.rst
@@ -0,0 +1,22 @@
+.. nghttp3 documentation master file, created by
+ sphinx-quickstart on Mon Nov 30 22:15:12 2020.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to nghttp3's documentation!
+===================================
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Contents:
+
+ programmers-guide
+ qpack-howto
+ apiref
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/source/programmers-guide.rst b/doc/source/programmers-guide.rst
new file mode 100644
index 0000000..d47d19c
--- /dev/null
+++ b/doc/source/programmers-guide.rst
@@ -0,0 +1,207 @@
+The nghttp3 programmers' guide
+==============================
+
+This document describes a basic usage of nghttp3 library and common
+pitfalls which programmers might encounter.
+
+Assumptions
+-----------
+
+nghttp3 is a thin HTTP/3 layer over an underlying QUIC stack. It
+relies on an underlying QUIC stack for flow control and connection
+management. Although nghttp3 is QUIC stack agnostic, it expects some
+particular interfaces from QUIC stack. We will describe them below.
+
+QPACK operations are done behind the scenes. Application can use
+:type:`nghttp3_settings` to change the behaviour of QPACK
+encoder/decoder.
+
+We define some keywords to avoid ambiguity in this document:
+
+* HTTP payload: HTTP request/response body
+* HTTP stream data: Series of HTTP header fields, HTTP payload, and
+ HTTP trailer fields, serialized into HTTP/3 wire format, which is
+ passed to or received from QUIC stack.
+
+Initialization
+--------------
+
+The :type:`nghttp3_conn` is a basic building block of nghttp3 library.
+It is created per HTTP/3 connection. If an endpoint is a client, use
+`nghttp3_conn_client_new` to initialize it as client. If it is a
+server, use `nghttp3_conn_server_new` to initialize it as server.
+
+Those initialization functions take :type:`nghttp3_callbacks`. All
+callbacks are optional, but setting no callback functions makes
+nghttp3 library useless for the most cases. We list callbacks which
+effectively required to do HTTP/3 transaction below:
+
+* :member:`acked_stream_data <nghttp3_callbacks.acked_stream_data>`:
+ Application has to retain HTTP payload (HTTP request/response body)
+ until they are no longer used by :type:`nghttp3_conn`. This
+ callback functions tells the largest offset of HTTP payload
+ acknowledged by a remote endpoint, and no longer used.
+* :member:`stream_close <nghttp3_callbacks.stream_close>`: It is
+ called when a stream is closed. It is useful to free resources
+ allocated for a stream.
+* :member:`recv_data <nghttp3_callbacks.recv_data>`: It is called when
+ HTTP payload (HTTP request/response body) is received.
+* :member:`deferred_consume <nghttp3_callbacks.deferred_consume>`: It
+ is called when :type:`nghttp3_conn` consumed HTTP stream data which
+ had been blocked for synchronization between streams. Application
+ has to tell QUIC stack the number of bytes consumed which affects
+ flow control. We will discuss more about this callback later when
+ explaining `nghttp3_conn_read_stream`.
+* :member:`recv_header <nghttp3_callbacks.recv_header>`: It is called
+ when an HTTP header field is received.
+* :member:`send_stop_sending <nghttp3_callbacks.send_stop_sending>`:
+ It is called when QUIC STOP_SENDING frame must be sent for a
+ particular stream. Sending STOP_SENDING frame means that
+ :type:`nghttp3_conn` no longer reads an incoming data for a
+ particular stream. Application has to tell QUIC stack to send
+ STOP_SENDING frame.
+* :member:`reset_stream <nghttp3_callbacks.reset_stream>`: It is
+ called when QUIC RESET_STREAM frame must be sent for a particular
+ stream. Sending RESET_STREAM frame means that :type:`nghttp3_conn`
+ stops sending any HTTP stream data to a particular stream.
+ Application has to tell QUIC stack to send RESET_STREAM frame.
+
+The initialization functions also takes :type:`nghttp3_settings` which
+is a set of options to tweak HTTP3/ connection settings.
+`nghttp3_settings_default` fills the default values.
+
+The *user_data* parameter to the initialization function is an opaque
+pointer and it is passed to callback functions.
+
+Binding control streams
+-----------------------
+
+HTTP/3 requires at least 3 local unidirectional streams for a control
+stream and QPACK encoder/decoder streams.
+
+Use the following functions to bind those streams to their purposes:
+
+* `nghttp3_conn_bind_control_stream`: Bind a given stream ID to a HTTP
+ control stream.
+* `nghttp3_conn_bind_qpack_streams`: Bind given 2 stream IDs to QPACK
+ encoder and decoder streams.
+
+Reading HTTP stream data
+------------------------
+
+`nghttp3_conn_read_stream` reads HTTP stream data from a particular
+stream. It returns the number of bytes "consumed". "Consumed" means
+that the those bytes are completely processed and QUIC stack can
+increase the flow control credit of both stream and connection by that
+amount.
+
+The HTTP payload notified by :member:`nghttp3_callbacks.recv_data` is
+not included in the return value. This is because the consumption of
+those data is done by application and nghttp3 library does not know
+when that happens.
+
+Some HTTP stream data might be consumed later because of
+synchronization between streams. In this case, those bytes are
+notified by :member:`nghttp3_callbacks.deferred_consume`.
+
+In every case, the number of consumed HTTP stream data must be
+notified to QUIC stack so that it can extend flow control limits.
+
+Writing HTTP stream data
+------------------------
+
+`nghttp3_conn_writev_stream` writes HTTP stream data to a particular
+stream. The order of streams to produce HTTP stream data is
+determined by the nghttp3 library. In general, the control streams
+have higher priority. The regular HTTP streams are ordered by
+header-based HTTP priority (see
+https://tools.ietf.org/html/draft-ietf-httpbis-priority-03).
+
+When HTTP stream data is generated, its stream ID is assigned to
+*\*pstream_id*. The pointer to HTTP stream data is assigned to *vec*,
+and the function returns the number of *vec* it filled. If the
+generated data is the final part of the stream, *\*pfin* gets nonzero
+value. If no HTTP stream data is generated, the function returns 0
+and *\*pstream_id* gets -1.
+
+The function might return 0 and *\*pstream_id* has proper stream ID
+and *\*pfin* set to nonzero. In this case, no data is written, but it
+signals the end of the stream. Even though no data is written, QUIC
+stack should be notified of the end of the stream.
+
+The produced HTTP stream data is passed to QUIC stack. Then call
+`nghttp3_conn_add_write_offset` with the number of bytes accepted by
+QUIC stack. This must be done even when the written data is 0 bytes
+with fin (refer to the previous paragraph for this corner case).
+
+If QUIC stack indicates that a stream is blocked by stream level flow
+control limit, call `nghttp3_conn_block_stream`. It makes the library
+not to generate HTTP stream data for the stream. Call
+`nghttp3_conn_unblock_stream` when stream level flow control limit is
+increased.
+
+If QUIC stack indicates that the write side of stream is closed, call
+`nghttp3_conn_shutdown_stream_write` instead of
+`nghttp3_conn_block_stream` so that the stream never be scheduled in
+the future.
+
+Creating HTTP request or response
+---------------------------------
+
+In order to create HTTP request, client application calls
+`nghttp3_conn_submit_request`. :type:`nghttp3_data_reader` is used to
+send HTTP payload (HTTP request body).
+
+Similarly, server application calls `nghttp3_conn_submit_response` to
+create HTTP response. :type:`nghttp3_data_reader` is also used to
+send HTTP payload (HTTP response body).
+
+In both cases, if :type:`nghttp3_data_reader` is not provided, no HTTP
+payload is generated.
+
+The :member:`nghttp3_data_reader.read_data` is a callback function to
+generate HTTP payload. Application must retain the data passed to the
+library until those data are acknowledged by
+:member:`nghttp3_callbacks.acked_stream_data`. When no data is
+available but will become available in the future, application returns
+:macro:`NGHTTP3_ERR_WOULDBLOCK` from this callback. Then the callback
+is not called for the particular stream until
+`nghttp3_conn_resume_stream` is called.
+
+Reading HTTP request or response
+--------------------------------
+
+The :member:`nghttp3_callbacks.recv_header` is called when an HTTP
+header field is received.
+
+The :member:`nghttp3_callbacks.recv_data` is called when HTTP payload
+is received.
+
+Acknowledgement of HTTP stream data
+-----------------------------------
+
+QUIC stack must provide an interface to notify the amount of data
+acknowledged by a remote endpoint. `nghttp3_conn_add_ack_offset` must
+be called with the largest offset of acknowledged HTTP stream data.
+
+Handling QUIC stream events
+---------------------------
+
+If underlying QUIC stream is closed, call `nghttp3_conn_close_stream`.
+
+If underlying QUIC stream is reset by a remote endpoint (that is when
+RESET_STREAM is received) or no longer read by a local endpoint (that
+is when STOP_SENDING is sent), call
+`nghttp3_conn_shutdown_stream_read`.
+
+Closing HTTP/3 connection gracefully
+------------------------------------
+
+`nghttp3_conn_submit_shutdown_notice` creates a message to a remote
+endpoint that HTTP/3 connection is going down. The receiving endpoint
+should stop sending HTTP request after reading this signal. After a
+couple of RTTs, call `nghttp3_conn_submit_shutdown` to start graceful
+shutdown. After calling this function, the local endpoint starts
+rejecting new incoming streams. The existing streams are processed
+normally. When all those streams are completely processed, the
+connection can be closed.
diff --git a/doc/source/qpack-howto.rst b/doc/source/qpack-howto.rst
new file mode 100644
index 0000000..a38d285
--- /dev/null
+++ b/doc/source/qpack-howto.rst
@@ -0,0 +1,85 @@
+QPACK How-To
+============
+
+Using QPACK encoder
+-------------------
+
+Firstly, create QPACK encoder by calling `nghttp3_qpack_encoder_new`.
+It requires *hard_max_dtable_size* parameter. When in doubt, pass
+4096 for this tutorial. Optionally, call
+`nghttp3_qpack_encoder_set_max_dtable_capacity` to set the maximum
+size of dynamic table. You can also call
+`nghttp3_qpack_encoder_set_max_blocked_streams` to set the maximum
+number of streams that can be blocked.
+
+In order to encode HTTP header fields, they must be stored in an array
+of :type:`nghttp3_nv`. Then call `nghttp3_qpack_encoder_encode`. It
+writes 3 buffers; *pbuf*, *rbuf*, and *ebuf*. They are a header block
+prefix, request stream, and encoder stream respectively. A header
+block prefix and request stream must be sent in this order to a stream
+denoted by *stream_id* passed to the function. Encoder stream must be
+sent to the encoder stream you setup.
+
+In order to read decoder stream, call
+`nghttp3_qpack_encoder_read_decoder`.
+
+Once QPACK encoder is no longer used, call `nghttp3_qpack_encoder_del`
+to free up memory allocated for it.
+
+Using QPACK decoder
+-------------------
+
+`nghttp3_qpack_decoder_new` will create new QPACK decoder. It
+requires *hard_max_dtable_size* and *max_blocked* parameters. When in
+doubt, pass 4096 and 0 respectively for this tutorial.
+
+In order to read encoder stream, call
+`nghttp3_qpack_decoder_read_encoder`. This might update dynamic
+table, but does not emit any header fields.
+
+In order to read request stream, call
+`nghttp3_qpack_decoder_read_request`. This is the function to emit
+header fields. *sctx* stores a per-stream decoder state and must be
+created by `nghttp3_qpack_stream_context_new`. It identifies a single
+encoded header block in a particular request stream. *fin* must be
+nonzero if and only if a passed data contains the last part of encoded
+header block.
+
+The scope of :type:`nghttp3_qpack_stream_context` is per header block,
+but `nghttp3_qpack_stream_context_reset` resets its state and can be
+reused for an another header block in the same stream. In general,
+you can reset it when you see that
+:macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` is set in *\*pflags*. When
+:type:`nghttp3_qpack_stream_context` is no longer necessary, call
+`nghttp3_qpack_stream_context_del` to free up its resource.
+
+`nghttp3_qpack_decoder_read_request` succeeds, *\*pflags* is assigned.
+If it has :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a header field
+is emitted and stored in the buffer pointed by *nv*. If *\*pflags*
+has :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, all header fields
+have been successfully decoded. If *\*pflags* has
+:macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked
+due to required insert count, which means that more data must be read
+by `nghttp3_qpack_decoder_read_encoder`.
+
+`nghttp3_qpack_decoder_read_request` returns the number of bytes read.
+When a header field is emitted, it might read data partially.
+Applciation has to call the function repeatedly by adjusting the
+pointer to data and its length until the function consumes all data or
+:macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` is set in *\*pflags*.
+
+If *nv* is assigned, its :member:`nv->name <nghttp3_qpack_nv.name>`
+and :member:`nv->value <nghttp3_qpack_nv.value>` are reference counted
+and already incremented by 1. If application finishes processing
+these values, it must call `nghttp3_rcbuf_decref(nv->name)
+<nghttp3_rcbuf_decref>` and `nghttp3_rcbuf_decref(nv->value)
+<nghttp3_rcbuf_decref>`.
+
+If an application has no interest to decode header fields for a
+particular stream, call `nghttp3_qpack_decoder_cancel_stream`.
+
+In order to tell decoding state to an encoder, QPACK decoder has to
+write decoder stream by calling `nghttp3_qpack_decoder_write_decoder`.
+
+Once QPACK decoder is no longer used, call `nghttp3_qpack_decoder_del`
+to free up memory allocated for it.
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..20a9df3
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1 @@
+/qpack
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..31ec1aa
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,50 @@
+# ngtcp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2017 ngtcp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(ENABLE_EXAMPLES)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/lib/includes
+ ${CMAKE_BINARY_DIR}/lib/includes
+ )
+
+ link_libraries(
+ nghttp3
+ )
+
+ set(qpack_SOURCES
+ qpack.cc
+ qpack_encode.cc
+ qpack_decode.cc
+ util.cc
+ )
+
+ add_executable(qpack ${qpack_SOURCES})
+ set_target_properties(qpack PROPERTIES
+ COMPILE_FLAGS "${WARNCXXFLAGS}"
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ )
+
+ # TODO prevent qpack example from being installed?
+endif()
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..8c90892
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,46 @@
+# ngtcp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2017 ngtcp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+EXTRA_DIST = CMakeLists.txt
+
+if ENABLE_EXAMPLES
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS)
+AM_CXXFLAGS = $(WARNCXXFLAGS) $(DEBUGCFLAGS)
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/lib/includes \
+ -I$(top_builddir)/lib/includes \
+ @DEFS@
+AM_LDFLAGS = -no-install
+LDADD = $(top_builddir)/lib/libnghttp3.la
+
+noinst_PROGRAMS = qpack
+
+qpack_SOURCES = \
+ qpack.cc qpack.h \
+ qpack_encode.cc qpack_encode.h \
+ qpack_decode.cc qpack_decode.h \
+ template.h \
+ util.cc util.h
+
+endif # ENABLE_EXAMPLES
diff --git a/examples/qpack.cc b/examples/qpack.cc
new file mode 100644
index 0000000..1353ad9
--- /dev/null
+++ b/examples/qpack.cc
@@ -0,0 +1,143 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "qpack.h"
+
+#include <cstring>
+#include <iostream>
+#include <string>
+
+#include <getopt.h>
+
+#include "qpack_encode.h"
+#include "qpack_decode.h"
+
+namespace nghttp3 {
+
+Config config{};
+
+namespace {
+void print_usage() {
+ std::cerr << "Usage: qpack [OPTIONS] <COMMAND> <INFILE> <OUTFILE>"
+ << std::endl;
+}
+} // namespace
+
+namespace {
+void print_help() {
+ print_usage();
+
+ std::cerr << R"(
+ <COMMAND> "encode" or "decode"
+ <INFILE> Path to an input file
+ <OUTFILE> Path to an output file
+Options:
+ -h, --help Display this help and exit.
+ -m, --max-blocked=<N>
+ The maximum number of streams which are permitted to be blocked.
+ -s, --max-dtable-size=<N>
+ The maximum size of dynamic table.
+ -a, --immediate-ack
+ Turn on immediate acknowlegement.
+)";
+}
+} // namespace
+
+int main(int argc, char **argv) {
+ for (;;) {
+ static int flag = 0;
+ (void)flag;
+ constexpr static option long_opts[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {"max-blocked", required_argument, nullptr, 'm'},
+ {"max-dtable-size", required_argument, nullptr, 's'},
+ {"immediate-ack", no_argument, nullptr, 'a'},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ auto optidx = 0;
+ auto c = getopt_long(argc, argv, "hm:s:a", long_opts, &optidx);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 'h':
+ // --help
+ print_help();
+ exit(EXIT_SUCCESS);
+ case 'm': {
+ // --max-blocked
+ config.max_blocked = strtoul(optarg, nullptr, 10);
+ break;
+ }
+ case 's': {
+ // --max-dtable-size
+ config.max_dtable_size = strtoul(optarg, nullptr, 10);
+ break;
+ }
+ case 'a':
+ // --immediate-ack
+ config.immediate_ack = true;
+ break;
+ case '?':
+ print_usage();
+ exit(EXIT_FAILURE);
+ case 0:
+ break;
+ default:
+ break;
+ };
+ }
+
+ if (argc - optind < 3) {
+ std::cerr << "Too few arguments" << std::endl;
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ auto command = std::string_view(argv[optind++]);
+ auto infile = std::string_view(argv[optind++]);
+ auto outfile = std::string_view(argv[optind++]);
+
+ int rv;
+ if (command == "encode") {
+ rv = encode(outfile, infile);
+ } else if (command == "decode") {
+ rv = decode(outfile, infile);
+ } else {
+ std::cerr << "Unrecognized command: " << command << std::endl;
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if (rv != 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+} // namespace nghttp3
+
+int main(int argc, char **argv) { return nghttp3::main(argc, argv); }
diff --git a/examples/qpack.h b/examples/qpack.h
new file mode 100644
index 0000000..d0b0ce6
--- /dev/null
+++ b/examples/qpack.h
@@ -0,0 +1,44 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef QPACK_H
+#define QPACK_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+namespace nghttp3 {
+
+struct Config {
+ size_t max_blocked;
+ size_t max_dtable_size;
+ bool immediate_ack;
+};
+
+} // namespace nghttp3
+
+#endif // QPACK_H
diff --git a/examples/qpack_decode.cc b/examples/qpack_decode.cc
new file mode 100644
index 0000000..22e44c8
--- /dev/null
+++ b/examples/qpack_decode.cc
@@ -0,0 +1,299 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "qpack_decode.h"
+
+#include <arpa/inet.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <cassert>
+#include <cstring>
+#include <cerrno>
+#include <iostream>
+#include <fstream>
+
+#include "qpack.h"
+#include "template.h"
+#include "util.h"
+
+namespace nghttp3 {
+
+extern Config config;
+
+Request::Request(int64_t stream_id, const nghttp3_buf *buf)
+ : buf(*buf), stream_id(stream_id) {
+ auto mem = nghttp3_mem_default();
+ nghttp3_qpack_stream_context_new(&sctx, stream_id, mem);
+}
+
+Request::~Request() { nghttp3_qpack_stream_context_del(sctx); }
+
+Decoder::Decoder(size_t max_dtable_size, size_t max_blocked)
+ : mem_(nghttp3_mem_default()),
+ dec_(nullptr),
+ max_dtable_size_(max_dtable_size),
+ max_blocked_(max_blocked) {}
+
+Decoder::~Decoder() { nghttp3_qpack_decoder_del(dec_); }
+
+int Decoder::init() {
+ if (auto rv = nghttp3_qpack_decoder_new(&dec_, max_dtable_size_, max_blocked_,
+ mem_);
+ rv != 0) {
+ std::cerr << "nghttp3_qpack_decoder_new: " << nghttp3_strerror(rv)
+ << std::endl;
+ return -1;
+ }
+
+ if (auto rv =
+ nghttp3_qpack_decoder_set_max_dtable_capacity(dec_, max_dtable_size_);
+ rv != 0) {
+ std::cerr << "nghttp3_qpack_decoder_set_max_dtable_capacity: "
+ << nghttp3_strerror(rv) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int Decoder::read_encoder(nghttp3_buf *buf) {
+ auto nread =
+ nghttp3_qpack_decoder_read_encoder(dec_, buf->pos, nghttp3_buf_len(buf));
+ if (nread < 0) {
+ std::cerr << "nghttp3_qpack_decoder_read_encoder: "
+ << nghttp3_strerror(nread) << std::endl;
+ return -1;
+ }
+
+ assert(static_cast<size_t>(nread) == nghttp3_buf_len(buf));
+
+ return 0;
+}
+
+std::tuple<Headers, int> Decoder::read_request(nghttp3_buf *buf,
+ int64_t stream_id) {
+ auto req = std::make_shared<Request>(stream_id, buf);
+
+ auto [headers, rv] = read_request(*req);
+ if (rv == -1) {
+ return {Headers{}, -1};
+ }
+ if (rv == 1) {
+ if (blocked_reqs_.size() >= max_blocked_) {
+ std::cerr << "Too many blocked streams: max_blocked=" << max_blocked_
+ << std::endl;
+ return {Headers{}, -1};
+ }
+ blocked_reqs_.emplace(std::move(req));
+ return {Headers{}, 1};
+ }
+ return {headers, 0};
+}
+
+std::tuple<Headers, int> Decoder::read_request(Request &req) {
+ nghttp3_qpack_nv nv;
+ uint8_t flags;
+ Headers headers;
+
+ for (;;) {
+ auto nread = nghttp3_qpack_decoder_read_request(
+ dec_, req.sctx, &nv, &flags, req.buf.pos, nghttp3_buf_len(&req.buf), 1);
+ if (nread < 0) {
+ std::cerr << "nghttp3_qpack_decoder_read_request: "
+ << nghttp3_strerror(nread) << std::endl;
+ return {Headers{}, -1};
+ }
+
+ req.buf.pos += nread;
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ break;
+ }
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) {
+ return {Headers{}, 1};
+ }
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
+ auto name = nghttp3_rcbuf_get_buf(nv.name);
+ auto value = nghttp3_rcbuf_get_buf(nv.value);
+ headers.emplace_back(std::string{name.base, name.base + name.len},
+ std::string{value.base, value.base + value.len});
+ nghttp3_rcbuf_decref(nv.name);
+ nghttp3_rcbuf_decref(nv.value);
+ }
+ }
+
+ return {headers, 0};
+}
+
+std::tuple<int64_t, Headers, int> Decoder::process_blocked() {
+ if (!blocked_reqs_.empty()) {
+ auto &top = blocked_reqs_.top();
+ if (nghttp3_qpack_stream_context_get_ricnt(top->sctx) >
+ nghttp3_qpack_decoder_get_icnt(dec_)) {
+ return {-1, {}, 0};
+ }
+
+ auto req = top;
+ blocked_reqs_.pop();
+
+ auto [headers, rv] = read_request(*req);
+ if (rv < 0) {
+ return {-1, {}, -1};
+ }
+ assert(rv == 0);
+
+ return {req->stream_id, headers, 0};
+ }
+ return {-1, {}, 0};
+}
+
+size_t Decoder::get_num_blocked() const { return blocked_reqs_.size(); }
+
+namespace {
+void write_header(
+ std::ostream &out,
+ const std::vector<std::pair<std::string, std::string>> &headers) {
+ for (auto &nv : headers) {
+ out.write(nv.first.c_str(), nv.first.size());
+ out.put('\t');
+ out.write(nv.second.c_str(), nv.second.size());
+ out.put('\n');
+ }
+ out.put('\n');
+}
+} // namespace
+
+int decode(const std::string_view &outfile, const std::string_view &infile) {
+ auto fd = open(infile.data(), O_RDONLY);
+ if (fd == -1) {
+ std::cerr << "Could not open " << infile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto fd_closer = defer(close, fd);
+
+ struct stat st;
+ if (fstat(fd, &st) == -1) {
+ std::cerr << "fstat: " << strerror(errno) << std::endl;
+ return -1;
+ }
+
+ auto out = std::ofstream(outfile.data(), std::ios::trunc | std::ios::binary);
+ if (!out) {
+ std::cerr << "Could not open file " << outfile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto in = reinterpret_cast<uint8_t *>(
+ mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0));
+ if (in == MAP_FAILED) {
+ std::cerr << "mmap: " << strerror(errno) << std::endl;
+ return -1;
+ }
+
+ auto unmapper = defer(munmap, in, st.st_size);
+
+ auto dec = Decoder(config.max_dtable_size, config.max_blocked);
+ if (auto rv = dec.init(); rv != 0) {
+ return rv;
+ }
+
+ for (auto p = in, end = in + st.st_size; p != end;) {
+ int64_t stream_id;
+ uint32_t size;
+
+ if (static_cast<size_t>(end - p) < sizeof(stream_id) + sizeof(size)) {
+ std::cerr << "Could not read stream ID and size" << std::endl;
+ return -1;
+ }
+
+ memcpy(&stream_id, p, sizeof(stream_id));
+ stream_id = nghttp3_ntohl64(stream_id);
+ p += sizeof(stream_id);
+
+ memcpy(&size, p, sizeof(size));
+ size = ntohl(size);
+ p += sizeof(size);
+
+ if ((size_t)(end - p) < size) {
+ std::cerr << "Insufficient input: require " << size << " but "
+ << (end - p) << " is available" << std::endl;
+ return -1;
+ }
+
+ nghttp3_buf buf;
+ buf.begin = buf.pos = p;
+ buf.end = buf.last = p + size;
+
+ p += size;
+
+ if (stream_id == 0) {
+ if (auto rv = dec.read_encoder(&buf); rv != 0) {
+ return rv;
+ }
+
+ for (;;) {
+ auto [stream_id, headers, rv] = dec.process_blocked();
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (stream_id == -1) {
+ break;
+ }
+
+ write_header(out, headers);
+ }
+
+ continue;
+ }
+
+ auto [headers, rv] = dec.read_request(&buf, stream_id);
+ if (rv == -1) {
+ return rv;
+ }
+ if (rv == 1) {
+ // Stream blocked
+ continue;
+ }
+
+ write_header(out, headers);
+ }
+
+ if (auto n = dec.get_num_blocked(); n) {
+ std::cerr << "Still " << n << " stream(s) blocked" << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+} // namespace nghttp3
diff --git a/examples/qpack_decode.h b/examples/qpack_decode.h
new file mode 100644
index 0000000..8641b3a
--- /dev/null
+++ b/examples/qpack_decode.h
@@ -0,0 +1,93 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef QPACK_DECODE_H
+#define QPACK_DECODE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include <vector>
+#include <queue>
+#include <functional>
+#include <utility>
+#include <memory>
+#include <string>
+
+namespace nghttp3 {
+struct Request {
+ Request(int64_t stream_id, const nghttp3_buf *buf);
+ ~Request();
+
+ nghttp3_buf buf;
+ nghttp3_qpack_stream_context *sctx;
+ int64_t stream_id;
+};
+} // namespace nghttp3
+
+namespace std {
+template <> struct greater<std::shared_ptr<nghttp3::Request>> {
+ bool operator()(const std::shared_ptr<nghttp3::Request> &lhs,
+ const std::shared_ptr<nghttp3::Request> &rhs) const {
+ return nghttp3_qpack_stream_context_get_ricnt(lhs->sctx) >
+ nghttp3_qpack_stream_context_get_ricnt(rhs->sctx);
+ }
+};
+} // namespace std
+
+namespace nghttp3 {
+
+using Headers = std::vector<std::pair<std::string, std::string>>;
+
+class Decoder {
+public:
+ Decoder(size_t max_dtable_size, size_t max_blocked);
+ ~Decoder();
+
+ int init();
+ int read_encoder(nghttp3_buf *buf);
+ std::tuple<Headers, int> read_request(nghttp3_buf *buf, int64_t stream_id);
+ std::tuple<Headers, int> read_request(Request &req);
+ std::tuple<int64_t, Headers, int> process_blocked();
+ size_t get_num_blocked() const;
+
+private:
+ const nghttp3_mem *mem_;
+ nghttp3_qpack_decoder *dec_;
+ std::priority_queue<std::shared_ptr<Request>,
+ std::vector<std::shared_ptr<Request>>,
+ std::greater<std::shared_ptr<Request>>>
+ blocked_reqs_;
+ size_t max_dtable_size_;
+ size_t max_blocked_;
+};
+
+int decode(const std::string_view &outfile, const std::string_view &infile);
+
+} // namespace nghttp3
+
+#endif // QPACK_ENCODE_H
diff --git a/examples/qpack_encode.cc b/examples/qpack_encode.cc
new file mode 100644
index 0000000..867a085
--- /dev/null
+++ b/examples/qpack_encode.cc
@@ -0,0 +1,221 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "qpack_encode.h"
+
+#include <arpa/inet.h>
+
+#include <cerrno>
+#include <cstring>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+#include <array>
+#include <iomanip>
+#include <vector>
+
+#include "qpack.h"
+#include "template.h"
+#include "util.h"
+
+namespace nghttp3 {
+
+extern Config config;
+
+Encoder::Encoder(size_t max_dtable_size, size_t max_blocked, bool immediate_ack)
+ : mem_(nghttp3_mem_default()),
+ enc_(nullptr),
+ max_dtable_size_(max_dtable_size),
+ max_blocked_(max_blocked),
+ immediate_ack_(immediate_ack) {}
+
+Encoder::~Encoder() { nghttp3_qpack_encoder_del(enc_); }
+
+int Encoder::init() {
+ int rv;
+
+ rv = nghttp3_qpack_encoder_new(&enc_, max_dtable_size_, mem_);
+ if (rv != 0) {
+ std::cerr << "nghttp3_qpack_encoder_new: " << nghttp3_strerror(rv)
+ << std::endl;
+ return -1;
+ }
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(enc_, max_dtable_size_);
+ nghttp3_qpack_encoder_set_max_blocked_streams(enc_, max_blocked_);
+
+ return 0;
+}
+
+int Encoder::encode(nghttp3_buf *pbuf, nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t stream_id, const nghttp3_nv *nva, size_t len) {
+ auto rv =
+ nghttp3_qpack_encoder_encode(enc_, pbuf, rbuf, ebuf, stream_id, nva, len);
+ if (rv != 0) {
+ std::cerr << "nghttp3_qpack_encoder_encode: " << nghttp3_strerror(rv)
+ << std::endl;
+ return -1;
+ }
+ if (immediate_ack_) {
+ nghttp3_qpack_encoder_ack_everything(enc_);
+ }
+ return 0;
+}
+
+namespace {
+void write_encoder_stream(std::ostream &out, nghttp3_buf *ebuf) {
+ uint64_t stream_id = 0;
+ out.write(reinterpret_cast<char *>(&stream_id), sizeof(stream_id));
+ uint32_t size = htonl(nghttp3_buf_len(ebuf));
+ out.write(reinterpret_cast<char *>(&size), sizeof(size));
+ out.write(reinterpret_cast<char *>(ebuf->pos), nghttp3_buf_len(ebuf));
+}
+} // namespace
+
+namespace {
+void write_request_stream(std::ostream &out, int64_t stream_id,
+ nghttp3_buf *pbuf, nghttp3_buf *rbuf) {
+ stream_id = nghttp3_htonl64(stream_id);
+ out.write(reinterpret_cast<char *>(&stream_id), sizeof(stream_id));
+ uint32_t size = htonl(nghttp3_buf_len(pbuf) + nghttp3_buf_len(rbuf));
+ out.write(reinterpret_cast<char *>(&size), sizeof(size));
+ out.write(reinterpret_cast<char *>(pbuf->pos), nghttp3_buf_len(pbuf));
+ out.write(reinterpret_cast<char *>(rbuf->pos), nghttp3_buf_len(rbuf));
+}
+} // namespace
+
+int encode(const std::string_view &outfile, const std::string_view &infile) {
+ auto in = std::ifstream(infile.data(), std::ios::binary);
+
+ if (!in) {
+ std::cerr << "Could not open file " << infile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto out = std::ofstream(outfile.data(), std::ios::trunc | std::ios::binary);
+ if (!out) {
+ std::cerr << "Could not open file " << outfile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto enc =
+ Encoder(config.max_dtable_size, config.max_blocked, config.immediate_ack);
+ if (enc.init() != 0) {
+ return -1;
+ }
+
+ nghttp3_buf pbuf, rbuf, ebuf;
+ nghttp3_buf_init(&pbuf);
+ nghttp3_buf_init(&rbuf);
+ nghttp3_buf_init(&ebuf);
+
+ auto mem = nghttp3_mem_default();
+ auto pbufd = defer(nghttp3_buf_free, &pbuf, mem);
+ auto rbufd = defer(nghttp3_buf_free, &rbuf, mem);
+ auto ebufd = defer(nghttp3_buf_free, &ebuf, mem);
+
+ int64_t stream_id = 1;
+ std::array<std::string, 1024> sarray;
+
+ size_t srclen = 0;
+ size_t enclen = 0;
+ size_t rslen = 0;
+ size_t eslen = 0;
+
+ for (; in;) {
+ auto nva = std::vector<nghttp3_nv>();
+ for (std::string line; std::getline(in, line);) {
+ if (line == "") {
+ break;
+ }
+
+ if (sarray.size() == nva.size()) {
+ std::cerr << "Too many headers: " << nva.size() << std::endl;
+ return -1;
+ }
+
+ sarray[nva.size()] = line;
+ const auto &s = sarray[nva.size()];
+
+ auto d = s.find('\t');
+ if (d == std::string_view::npos) {
+ std::cerr << "Could not find TAB in " << s << std::endl;
+ return -1;
+ }
+ auto name = std::string_view(s.c_str(), d);
+ auto value = std::string_view(s.c_str() + d + 1, s.size() - d - 1);
+ value.remove_prefix(std::min(value.find_first_not_of(" "), value.size()));
+
+ srclen += name.size() + value.size();
+
+ nva.emplace_back(nghttp3_nv{
+ const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(name.data())),
+ const_cast<uint8_t *>(
+ reinterpret_cast<const uint8_t *>(value.data())),
+ name.size(), value.size()});
+ }
+
+ if (nva.empty()) {
+ break;
+ }
+
+ if (auto rv =
+ enc.encode(&pbuf, &rbuf, &ebuf, stream_id, nva.data(), nva.size());
+ rv != 0) {
+ return -1;
+ }
+
+ enclen += nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf) +
+ nghttp3_buf_len(&ebuf);
+
+ if (nghttp3_buf_len(&ebuf)) {
+ write_encoder_stream(out, &ebuf);
+ }
+ write_request_stream(out, stream_id, &pbuf, &rbuf);
+
+ rslen += nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf);
+ eslen += nghttp3_buf_len(&ebuf);
+
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ ++stream_id;
+ }
+
+ if (srclen == 0) {
+ std::cerr << "No header field processed" << std::endl;
+ } else {
+ std::cerr << srclen << " -> " << enclen << " (r:" << rslen
+ << " + e:" << eslen << ") " << std::fixed << std::setprecision(2)
+ << (1. - (static_cast<double>(enclen) / srclen)) * 100
+ << "% compressed" << std::endl;
+ }
+ return 0;
+}
+
+} // namespace nghttp3
diff --git a/examples/qpack_encode.h b/examples/qpack_encode.h
new file mode 100644
index 0000000..b310ecd
--- /dev/null
+++ b/examples/qpack_encode.h
@@ -0,0 +1,59 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef QPACK_ENCODE_H
+#define QPACK_ENCODE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include <string>
+
+namespace nghttp3 {
+
+class Encoder {
+public:
+ Encoder(size_t max_dtable_size, size_t max_blocked, bool immediate_ack);
+ ~Encoder();
+
+ int init();
+ int encode(nghttp3_buf *pbuf, nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t stream_id, const nghttp3_nv *nva, size_t len);
+
+private:
+ const nghttp3_mem *mem_;
+ nghttp3_qpack_encoder *enc_;
+ size_t max_dtable_size_;
+ size_t max_blocked_;
+ bool immediate_ack_;
+};
+
+int encode(const std::string_view &outfile, const std::string_view &infile);
+
+} // namespace nghttp3
+
+#endif // QPACK_ENCODE_H
diff --git a/examples/template.h b/examples/template.h
new file mode 100644
index 0000000..1f758b4
--- /dev/null
+++ b/examples/template.h
@@ -0,0 +1,77 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2015 ngttp2 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 TEMPLATE_H
+#define TEMPLATE_H
+
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+namespace nghttp3 {
+
+// inspired by <http://blog.korfuri.fr/post/go-defer-in-cpp/>, but our
+// template can take functions returning other than void.
+template <typename F, typename... T> struct Defer {
+ Defer(F &&f, T &&...t)
+ : f(std::bind(std::forward<F>(f), std::forward<T>(t)...)) {}
+ Defer(Defer &&o) noexcept : f(std::move(o.f)) {}
+ ~Defer() { f(); }
+
+ using ResultType = typename std::result_of<typename std::decay<F>::type(
+ typename std::decay<T>::type...)>::type;
+ std::function<ResultType()> f;
+};
+
+template <typename F, typename... T> Defer<F, T...> defer(F &&f, T &&...t) {
+ return Defer<F, T...>(std::forward<F>(f), std::forward<T>(t)...);
+}
+
+template <typename T, size_t N> constexpr size_t array_size(T (&)[N]) {
+ return N;
+}
+
+template <typename T, size_t N> constexpr size_t str_size(T (&)[N]) {
+ return N - 1;
+}
+
+// User-defined literals for K, M, and G (powers of 1024)
+
+constexpr unsigned long long operator"" _k(unsigned long long k) {
+ return k * 1024;
+}
+
+constexpr unsigned long long operator"" _m(unsigned long long m) {
+ return m * 1024 * 1024;
+}
+
+constexpr unsigned long long operator"" _g(unsigned long long g) {
+ return g * 1024 * 1024 * 1024;
+}
+
+} // namespace nghttp3
+
+#endif // TEMPLATE_H
diff --git a/examples/util.cc b/examples/util.cc
new file mode 100644
index 0000000..8973825
--- /dev/null
+++ b/examples/util.cc
@@ -0,0 +1,27 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "util.h"
+
+namespace nghttp3 {} // namespace nghttp3
diff --git a/examples/util.h b/examples/util.h
new file mode 100644
index 0000000..8e06086
--- /dev/null
+++ b/examples/util.h
@@ -0,0 +1,64 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef UTIL_H
+#define UTIL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdint.h>
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif /* HAVE_SYS_ENDIAN_H */
+
+namespace nghttp3 {
+
+#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH
+# define nghttp3_ntohl64(N) be64toh(N)
+# define nghttp3_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+# define nghttp3_bswap64(N) \
+ ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
+# define nghttp3_ntohl64(N) nghttp3_bswap64(N)
+# define nghttp3_htonl64(N) nghttp3_bswap64(N)
+#endif /* !HAVE_BE64TOH */
+
+} // namespace nghttp3
+
+#endif // UTIL_H
diff --git a/fuzz/corpus/fuzz_http3serverreq/curl b/fuzz/corpus/fuzz_http3serverreq/curl
new file mode 100644
index 0000000..15aa688
--- /dev/null
+++ b/fuzz/corpus/fuzz_http3serverreq/curl
Binary files differ
diff --git a/fuzz/corpus/fuzz_qpackdecoder/netbsd-hq.out.256.100.1 b/fuzz/corpus/fuzz_qpackdecoder/netbsd-hq.out.256.100.1
new file mode 100644
index 0000000..d55ee30
--- /dev/null
+++ b/fuzz/corpus/fuzz_qpackdecoder/netbsd-hq.out.256.100.1
Binary files differ
diff --git a/fuzz/fuzz_http3serverreq.cc b/fuzz/fuzz_http3serverreq.cc
new file mode 100644
index 0000000..98c82f0
--- /dev/null
+++ b/fuzz/fuzz_http3serverreq.cc
@@ -0,0 +1,67 @@
+#include <array>
+
+#include <nghttp3/nghttp3.h>
+
+static int send_data(nghttp3_conn *conn) {
+ std::array<nghttp3_vec, 16> vec;
+ int64_t stream_id;
+ int fin;
+
+ for (;;) {
+ auto veccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec.data(),
+ vec.size());
+ if (veccnt < 0) {
+ return 0;
+ }
+
+ if (veccnt || fin) {
+ auto ndatalen = nghttp3_vec_len(vec.data(), veccnt);
+
+ if (nghttp3_conn_add_write_offset(conn, stream_id, ndatalen) < 0) {
+ return 0;
+ }
+
+ if (nghttp3_conn_add_ack_offset(conn, stream_id, ndatalen) < 0) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ nghttp3_callbacks callbacks{};
+ nghttp3_settings settings;
+
+ nghttp3_settings_default(&settings);
+
+ nghttp3_conn *conn;
+ auto rv =
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, nullptr, nullptr);
+ if (rv != 0) {
+ return 0;
+ }
+
+ nghttp3_conn_set_max_client_streams_bidi(conn, 100);
+
+ nghttp3_ssize nread;
+
+ if (send_data(conn) != 0) {
+ goto fin;
+ }
+
+ nread = nghttp3_conn_read_stream(conn, 0, data, size, 0);
+ if (nread < 0) {
+ goto fin;
+ }
+
+ if (send_data(conn) != 0) {
+ goto fin;
+ }
+
+fin:
+ nghttp3_conn_del(conn);
+
+ return 0;
+}
diff --git a/fuzz/fuzz_qpackdecoder.cc b/fuzz/fuzz_qpackdecoder.cc
new file mode 100644
index 0000000..43bbe50
--- /dev/null
+++ b/fuzz/fuzz_qpackdecoder.cc
@@ -0,0 +1,250 @@
+#include <arpa/inet.h>
+
+#include <cassert>
+#include <cstring>
+#include <vector>
+#include <queue>
+#include <functional>
+#include <utility>
+#include <memory>
+#include <string>
+
+#include <nghttp3/nghttp3.h>
+
+#define nghttp3_ntohl64(N) be64toh(N)
+
+struct Request {
+ Request(int64_t stream_id, const nghttp3_buf *buf);
+ ~Request();
+
+ nghttp3_buf buf;
+ nghttp3_qpack_stream_context *sctx;
+ int64_t stream_id;
+};
+
+namespace std {
+template <> struct greater<std::shared_ptr<Request>> {
+ bool operator()(const std::shared_ptr<Request> &lhs,
+ const std::shared_ptr<Request> &rhs) const {
+ return nghttp3_qpack_stream_context_get_ricnt(lhs->sctx) >
+ nghttp3_qpack_stream_context_get_ricnt(rhs->sctx);
+ }
+};
+} // namespace std
+
+using Headers = std::vector<std::pair<std::string, std::string>>;
+
+class Decoder {
+public:
+ Decoder(size_t max_dtable_size, size_t max_blocked);
+ ~Decoder();
+
+ int init();
+ int read_encoder(nghttp3_buf *buf);
+ std::tuple<Headers, int> read_request(nghttp3_buf *buf, int64_t stream_id);
+ std::tuple<Headers, int> read_request(Request &req);
+ std::tuple<int64_t, Headers, int> process_blocked();
+ size_t get_num_blocked() const;
+
+private:
+ const nghttp3_mem *mem_;
+ nghttp3_qpack_decoder *dec_;
+ std::priority_queue<std::shared_ptr<Request>,
+ std::vector<std::shared_ptr<Request>>,
+ std::greater<std::shared_ptr<Request>>>
+ blocked_reqs_;
+ size_t max_dtable_size_;
+ size_t max_blocked_;
+};
+
+Request::Request(int64_t stream_id, const nghttp3_buf *buf)
+ : buf(*buf), stream_id(stream_id) {
+ auto mem = nghttp3_mem_default();
+ nghttp3_qpack_stream_context_new(&sctx, stream_id, mem);
+}
+
+Request::~Request() { nghttp3_qpack_stream_context_del(sctx); }
+
+Decoder::Decoder(size_t max_dtable_size, size_t max_blocked)
+ : mem_(nghttp3_mem_default()),
+ dec_(nullptr),
+ max_dtable_size_(max_dtable_size),
+ max_blocked_(max_blocked) {}
+
+Decoder::~Decoder() { nghttp3_qpack_decoder_del(dec_); }
+
+int Decoder::init() {
+ if (auto rv = nghttp3_qpack_decoder_new(&dec_, max_dtable_size_, max_blocked_,
+ mem_);
+ rv != 0) {
+ return -1;
+ }
+
+ nghttp3_qpack_decoder_set_max_dtable_capacity(dec_, max_dtable_size_);
+
+ return 0;
+}
+
+int Decoder::read_encoder(nghttp3_buf *buf) {
+ auto nread =
+ nghttp3_qpack_decoder_read_encoder(dec_, buf->pos, nghttp3_buf_len(buf));
+ if (nread < 0) {
+ return -1;
+ }
+
+ assert(static_cast<size_t>(nread) == nghttp3_buf_len(buf));
+
+ return 0;
+}
+
+std::tuple<Headers, int> Decoder::read_request(nghttp3_buf *buf,
+ int64_t stream_id) {
+ auto req = std::make_shared<Request>(stream_id, buf);
+
+ auto [headers, rv] = read_request(*req);
+ if (rv == -1) {
+ return {Headers{}, -1};
+ }
+ if (rv == 1) {
+ if (blocked_reqs_.size() >= max_blocked_) {
+ return {Headers{}, -1};
+ }
+ blocked_reqs_.emplace(std::move(req));
+ return {Headers{}, 1};
+ }
+ return {headers, 0};
+}
+
+std::tuple<Headers, int> Decoder::read_request(Request &req) {
+ nghttp3_qpack_nv nv;
+ uint8_t flags;
+ Headers headers;
+
+ for (;;) {
+ auto nread = nghttp3_qpack_decoder_read_request(
+ dec_, req.sctx, &nv, &flags, req.buf.pos, nghttp3_buf_len(&req.buf), 1);
+ if (nread < 0) {
+ return {Headers{}, -1};
+ }
+
+ req.buf.pos += nread;
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ break;
+ }
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) {
+ return {Headers{}, 1};
+ }
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
+ auto name = nghttp3_rcbuf_get_buf(nv.name);
+ auto value = nghttp3_rcbuf_get_buf(nv.value);
+ headers.emplace_back(std::string{name.base, name.base + name.len},
+ std::string{value.base, value.base + value.len});
+ nghttp3_rcbuf_decref(nv.name);
+ nghttp3_rcbuf_decref(nv.value);
+ }
+ }
+
+ return {headers, 0};
+}
+
+std::tuple<int64_t, Headers, int> Decoder::process_blocked() {
+ if (!blocked_reqs_.empty()) {
+ auto &top = blocked_reqs_.top();
+ if (nghttp3_qpack_stream_context_get_ricnt(top->sctx) >
+ nghttp3_qpack_decoder_get_icnt(dec_)) {
+ return {-1, {}, 0};
+ }
+
+ auto req = top;
+ blocked_reqs_.pop();
+
+ auto [headers, rv] = read_request(*req);
+ if (rv < 0) {
+ return {-1, {}, -1};
+ }
+ assert(rv == 0);
+
+ return {req->stream_id, headers, 0};
+ }
+ return {-1, {}, 0};
+}
+
+size_t Decoder::get_num_blocked() const { return blocked_reqs_.size(); }
+
+int decode(const uint8_t *data, size_t datalen) {
+ auto dec = Decoder(256, 100);
+ if (auto rv = dec.init(); rv != 0) {
+ return rv;
+ }
+
+ for (auto p = data, end = data + datalen; p != end;) {
+ int64_t stream_id;
+ uint32_t size;
+
+ if (static_cast<size_t>(end - p) < sizeof(stream_id) + sizeof(size)) {
+ return -1;
+ }
+
+ memcpy(&stream_id, p, sizeof(stream_id));
+ stream_id = nghttp3_ntohl64(stream_id);
+ p += sizeof(stream_id);
+
+ memcpy(&size, p, sizeof(size));
+ size = ntohl(size);
+ p += sizeof(size);
+
+ if ((size_t)(end - p) < size) {
+ return -1;
+ }
+
+ nghttp3_buf buf;
+ buf.begin = buf.pos = const_cast<uint8_t *>(p);
+ buf.end = buf.last = const_cast<uint8_t *>(p) + size;
+
+ p += size;
+
+ if (stream_id == 0) {
+ if (auto rv = dec.read_encoder(&buf); rv != 0) {
+ return rv;
+ }
+
+ for (;;) {
+ auto [stream_id, headers, rv] = dec.process_blocked();
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (stream_id == -1) {
+ break;
+ }
+
+ (void)headers;
+ }
+
+ continue;
+ }
+
+ auto [headers, rv] = dec.read_request(&buf, stream_id);
+ if (rv == -1) {
+ return rv;
+ }
+ if (rv == 1) {
+ // Stream blocked
+ continue;
+ }
+
+ (void)headers;
+ }
+
+ if (auto n = dec.get_num_blocked(); n) {
+ return -1;
+ }
+
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ decode(data, size);
+ return 0;
+}
diff --git a/genchartbl.py b/genchartbl.py
new file mode 100755
index 0000000..3529ede
--- /dev/null
+++ b/genchartbl.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+import sys
+import string
+
+def name(i):
+ if i < 0x21:
+ return \
+ ['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
+ 'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
+ 'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
+ 'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US ',
+ 'SPC '][i]
+ if i == 0x7f:
+ return 'DEL '
+
+def gentbl(tblname, pred):
+ sys.stdout.write('''\
+/* Generated by genchartbl.py */
+static const int {}[] = {{
+'''.format(tblname))
+
+ for i in range(256):
+ if pred(chr(i)):
+ v = 1
+ else:
+ v = 0
+
+ if 0x21 <= i and i < 0x7f:
+ sys.stdout.write('{} /* {} */, '.format(v, chr(i)))
+ elif 0x80 <= i:
+ sys.stdout.write('{} /* {} */, '.format(v, hex(i)))
+ else:
+ sys.stdout.write('{} /* {} */, '.format(v, name(i)))
+ if (i + 1)%4 == 0:
+ sys.stdout.write('\n')
+
+ sys.stdout.write('};\n')
+
+def sf_key():
+ gentbl('SF_KEY_CHARS', lambda c: c in string.ascii_lowercase or
+ c in string.digits or c in '_-.*')
+
+def sf_dquote():
+ gentbl('SF_DQUOTE_CHARS', lambda c: (0x20 <= ord(c) and ord(c) <= 0x21) or
+ (0x23 <= ord(c) and ord(c) <= 0x5b) or
+ (0x5d <= ord(c) and ord(c) <= 0x7e))
+
+def sf_token():
+ gentbl('SF_TOKEN_CHARS', lambda c: c in "!#$%&'*+-.^_`|~:/" or
+ c in string.digits or c in string.ascii_letters)
+
+def sf_byteseq():
+ gentbl('SF_BYTESEQ_CHARS', lambda c: c in string.ascii_letters or
+ c in string.digits or c in '+/=')
+
+sf_key()
+sys.stdout.write('\n')
+
+sf_dquote()
+sys.stdout.write('\n')
+
+sf_token()
+sys.stdout.write('\n')
+
+sf_byteseq()
+sys.stdout.write('\n')
diff --git a/genlibtokenlookup.py b/genlibtokenlookup.py
new file mode 100755
index 0000000..cd6163f
--- /dev/null
+++ b/genlibtokenlookup.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+
+HEADERS = [
+ (':authority', 0),
+ (':path', 1),
+ ('age', 2),
+ ('content-disposition', 3),
+ ('content-length', 4),
+ ('cookie', 5),
+ ('date', 6),
+ ('etag', 7),
+ ('if-modified-since', 8),
+ ('if-none-match', 9),
+ ('last-modified', 10),
+ ('link', 11),
+ ('location', 12),
+ ('referer', 13),
+ ('set-cookie', 14),
+ (':method', 15),
+ (':method', 16),
+ (':method', 17),
+ (':method', 18),
+ (':method', 19),
+ (':method', 20),
+ (':method', 21),
+ (':scheme', 22),
+ (':scheme', 23),
+ (':status', 24),
+ (':status', 25),
+ (':status', 26),
+ (':status', 27),
+ (':status', 28),
+ ('accept', 29),
+ ('accept', 30),
+ ('accept-encoding', 31),
+ ('accept-ranges', 32),
+ ('access-control-allow-headers', 33),
+ ('access-control-allow-headers', 34),
+ ('access-control-allow-origin', 35),
+ ('cache-control', 36),
+ ('cache-control', 37),
+ ('cache-control', 38),
+ ('cache-control', 39),
+ ('cache-control', 40),
+ ('cache-control', 41),
+ ('content-encoding', 42),
+ ('content-encoding', 43),
+ ('content-type', 44),
+ ('content-type', 45),
+ ('content-type', 46),
+ ('content-type', 47),
+ ('content-type', 48),
+ ('content-type', 49),
+ ('content-type', 50),
+ ('content-type', 51),
+ ('content-type', 52),
+ ('content-type', 53),
+ ('content-type', 54),
+ ('range', 55),
+ ('strict-transport-security', 56),
+ ('strict-transport-security', 57),
+ ('strict-transport-security', 58),
+ ('vary', 59),
+ ('vary', 60),
+ ('x-content-type-options', 61),
+ ('x-xss-protection', 62),
+ (':status', 63),
+ (':status', 64),
+ (':status', 65),
+ (':status', 66),
+ (':status', 67),
+ (':status', 68),
+ (':status', 69),
+ (':status', 70),
+ (':status', 71),
+ ('accept-language', 72),
+ ('access-control-allow-credentials', 73),
+ ('access-control-allow-credentials', 74),
+ ('access-control-allow-headers', 75),
+ ('access-control-allow-methods', 76),
+ ('access-control-allow-methods', 77),
+ ('access-control-allow-methods', 78),
+ ('access-control-expose-headers', 79),
+ ('access-control-request-headers', 80),
+ ('access-control-request-method', 81),
+ ('access-control-request-method', 82),
+ ('alt-svc', 83),
+ ('authorization', 84),
+ ('content-security-policy', 85),
+ ('early-data', 86),
+ ('expect-ct', 87),
+ ('forwarded', 88),
+ ('if-range', 89),
+ ('origin', 90),
+ ('purpose', 91),
+ ('server', 92),
+ ('timing-allow-origin', 93),
+ ('upgrade-insecure-requests', 94),
+ ('user-agent', 95),
+ ('x-forwarded-for', 96),
+ ('x-frame-options', 97),
+ ('x-frame-options', 98),
+ # Additional header fields for HTTP messaging validation
+ ('host', None),
+ ('connection', None),
+ ('keep-alive', None),
+ ('proxy-connection', None),
+ ('transfer-encoding', None),
+ ('upgrade', None),
+ ('te', None),
+ (':protocol', None),
+ ('priority', None),
+]
+
+def to_enum_hd(k):
+ res = 'NGHTTP3_QPACK_TOKEN_'
+ for c in k.upper():
+ if c == ':' or c == '-':
+ res += '_'
+ continue
+ res += c
+ return res
+
+def build_header(headers):
+ res = {}
+ for k, _ in headers:
+ size = len(k)
+ if size not in res:
+ res[size] = {}
+ ent = res[size]
+ c = k[-1]
+ if c not in ent:
+ ent[c] = []
+ if k not in ent[c]:
+ ent[c].append(k)
+
+ return res
+
+def gen_enum():
+ name = ''
+ print 'typedef enum {'
+ for k, token in HEADERS:
+ if token is None:
+ print ' {},'.format(to_enum_hd(k))
+ else:
+ if name != k:
+ name = k
+ print ' {} = {},'.format(to_enum_hd(k), token)
+ print '} nghttp3_qpack_token;'
+
+def gen_index_header():
+ print '''\
+static int32_t lookup_token(const uint8_t *name, size_t namelen) {
+ switch (namelen) {'''
+ b = build_header(HEADERS)
+ for size in sorted(b.keys()):
+ ents = b[size]
+ print '''\
+ case {}:'''.format(size)
+ print '''\
+ switch (name[{}]) {{'''.format(size - 1)
+ for c in sorted(ents.keys()):
+ headers = sorted(ents[c])
+ print '''\
+ case '{}':'''.format(c)
+ for k in headers:
+ print '''\
+ if (memeq("{}", name, {})) {{
+ return {};
+ }}'''.format(k[:-1], size - 1, to_enum_hd(k))
+ print '''\
+ break;'''
+ print '''\
+ }
+ break;'''
+ print '''\
+ }
+ return -1;
+}'''
+
+if __name__ == '__main__':
+ print '''/* Don't use nghttp3_qpack_token below. Use mkstatichdtbl.py instead */'''
+ gen_enum()
+ print ''
+ gen_index_header()
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..8022467
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,97 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3
+# Copyright (c) 2016 ngtcp2
+#
+# 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.
+
+add_subdirectory(includes)
+
+include_directories(
+ "${CMAKE_CURRENT_SOURCE_DIR}/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/includes"
+)
+
+add_definitions(-DBUILDING_NGHTTP3)
+
+set(nghttp3_SOURCES
+ nghttp3_rcbuf.c
+ nghttp3_mem.c
+ nghttp3_str.c
+ nghttp3_conv.c
+ nghttp3_buf.c
+ nghttp3_ringbuf.c
+ nghttp3_pq.c
+ nghttp3_map.c
+ nghttp3_ksl.c
+ nghttp3_qpack.c
+ nghttp3_qpack_huffman.c
+ nghttp3_qpack_huffman_data.c
+ nghttp3_err.c
+ nghttp3_debug.c
+ nghttp3_conn.c
+ nghttp3_stream.c
+ nghttp3_frame.c
+ nghttp3_tnode.c
+ nghttp3_vec.c
+ nghttp3_gaptr.c
+ nghttp3_idtr.c
+ nghttp3_range.c
+ nghttp3_http.c
+ nghttp3_version.c
+ nghttp3_balloc.c
+ nghttp3_opl.c
+ nghttp3_objalloc.c
+ nghttp3_unreachable.c
+)
+
+# Public shared library
+if(ENABLE_SHARED_LIB)
+ add_library(nghttp3 SHARED ${nghttp3_SOURCES})
+ set_target_properties(nghttp3 PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
+ C_VISIBILITY_PRESET hidden
+ )
+
+ install(TARGETS nghttp3
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif()
+
+if(HAVE_CUNIT OR ENABLE_STATIC_LIB)
+ # Static library (for unittests because of symbol visibility)
+ add_library(nghttp3_static STATIC ${nghttp3_SOURCES})
+ set_target_properties(nghttp3_static PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
+ ARCHIVE_OUTPUT_NAME nghttp3${STATIC_LIB_SUFFIX}
+ )
+ target_compile_definitions(nghttp3_static PUBLIC "-DNGHTTP3_STATICLIB")
+ if(ENABLE_STATIC_LIB)
+ install(TARGETS nghttp3_static
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+ endif()
+endif()
+
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp3.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..c9cc14b
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,97 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3
+# Copyright (c) 2016 ngtcp2
+#
+# 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.
+SUBDIRS = includes
+
+EXTRA_DIST = CMakeLists.txt
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP3
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libnghttp3.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LTLIBRARIES = libnghttp3.la
+
+OBJECTS = \
+ nghttp3_rcbuf.c \
+ nghttp3_mem.c \
+ nghttp3_str.c \
+ nghttp3_conv.c \
+ nghttp3_buf.c \
+ nghttp3_ringbuf.c \
+ nghttp3_pq.c \
+ nghttp3_map.c \
+ nghttp3_ksl.c \
+ nghttp3_qpack.c \
+ nghttp3_qpack_huffman.c \
+ nghttp3_qpack_huffman_data.c \
+ nghttp3_err.c \
+ nghttp3_debug.c \
+ nghttp3_conn.c \
+ nghttp3_stream.c \
+ nghttp3_frame.c \
+ nghttp3_tnode.c \
+ nghttp3_vec.c \
+ nghttp3_gaptr.c \
+ nghttp3_idtr.c \
+ nghttp3_range.c \
+ nghttp3_http.c \
+ nghttp3_version.c \
+ nghttp3_balloc.c \
+ nghttp3_opl.c \
+ nghttp3_objalloc.c \
+ nghttp3_unreachable.c
+HFILES = \
+ nghttp3_rcbuf.h \
+ nghttp3_mem.h \
+ nghttp3_str.h \
+ nghttp3_conv.h \
+ nghttp3_buf.h \
+ nghttp3_ringbuf.h \
+ nghttp3_pq.h \
+ nghttp3_map.h \
+ nghttp3_ksl.h \
+ nghttp3_qpack.h \
+ nghttp3_qpack_huffman.h \
+ nghttp3_err.h \
+ nghttp3_debug.h \
+ nghttp3_conn.h \
+ nghttp3_stream.h \
+ nghttp3_frame.h \
+ nghttp3_tnode.h \
+ nghttp3_vec.h \
+ nghttp3_gaptr.h \
+ nghttp3_idtr.h \
+ nghttp3_range.h \
+ nghttp3_http.h \
+ nghttp3_balloc.h \
+ nghttp3_opl.h \
+ nghttp3_objalloc.h \
+ nghttp3_unreachable.h \
+ nghttp3_macro.h
+
+libnghttp3_la_SOURCES = $(HFILES) $(OBJECTS)
+libnghttp3_la_LDFLAGS = -no-undefined \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
diff --git a/lib/includes/CMakeLists.txt b/lib/includes/CMakeLists.txt
new file mode 100644
index 0000000..76d7364
--- /dev/null
+++ b/lib/includes/CMakeLists.txt
@@ -0,0 +1,4 @@
+install(FILES
+ nghttp3/nghttp3.h
+ "${CMAKE_CURRENT_BINARY_DIR}/nghttp3/version.h"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nghttp3")
diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am
new file mode 100644
index 0000000..b69d2fc
--- /dev/null
+++ b/lib/includes/Makefile.am
@@ -0,0 +1,26 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2016 ngtcp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+EXTRA_DIST = CMakeLists.txt
+
+nobase_include_HEADERS = nghttp3/nghttp3.h nghttp3/version.h
diff --git a/lib/includes/nghttp3/nghttp3.h b/lib/includes/nghttp3/nghttp3.h
new file mode 100644
index 0000000..7986ea8
--- /dev/null
+++ b/lib/includes/nghttp3/nghttp3.h
@@ -0,0 +1,2646 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2018 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2017 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_H
+#define NGHTTP3_H
+
+/* Define WIN32 when build target is Win32 API (borrowed from
+ libcurl) */
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+/* MSVC < 2013 does not have inttypes.h because it is not C99
+ compliant. See compiler macros and version number in
+ https://sourceforge.net/p/predef/wiki/Compilers/ */
+# include <stdint.h>
+#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+# include <inttypes.h>
+#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <nghttp3/version.h>
+
+#ifdef NGHTTP3_STATICLIB
+# define NGHTTP3_EXTERN
+#elif defined(WIN32)
+# ifdef BUILDING_NGHTTP3
+# define NGHTTP3_EXTERN __declspec(dllexport)
+# else /* !BUILDING_NGHTTP3 */
+# define NGHTTP3_EXTERN __declspec(dllimport)
+# endif /* !BUILDING_NGHTTP3 */
+#else /* !defined(WIN32) */
+# ifdef BUILDING_NGHTTP3
+# define NGHTTP3_EXTERN __attribute__((visibility("default")))
+# else /* !BUILDING_NGHTTP3 */
+# define NGHTTP3_EXTERN
+# endif /* !BUILDING_NGHTTP3 */
+#endif /* !defined(WIN32) */
+
+/**
+ * @typedef
+ *
+ * :type:`nghttp3_ssize` is signed counterpart of size_t.
+ */
+typedef ptrdiff_t nghttp3_ssize;
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ALPN_H3` is a serialized form of HTTP/3 ALPN
+ * protocol identifier this library supports. Notice that the first
+ * byte is the length of the following protocol identifier.
+ */
+#define NGHTTP3_ALPN_H3 "\x2h3"
+
+/**
+ * @macrosection
+ *
+ * nghttp3 library error codes
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` indicates that a passed
+ * argument is invalid.
+ */
+#define NGHTTP3_ERR_INVALID_ARGUMENT -101
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_NOBUF` indicates that a provided buffer does
+ * not have enough space to store data.
+ */
+#define NGHTTP3_ERR_NOBUF -102
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE` indicates that a requested
+ * operation is not allowed at the current connection state.
+ */
+#define NGHTTP3_ERR_INVALID_STATE -103
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK` indicates that an operation might
+ * block.
+ */
+#define NGHTTP3_ERR_WOULDBLOCK -104
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_IN_USE` indicates that a stream ID is
+ * already in use.
+ */
+#define NGHTTP3_ERR_STREAM_IN_USE -105
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_HEADER` indicates that an HTTP
+ * header field is malformed.
+ */
+#define NGHTTP3_ERR_MALFORMED_HTTP_HEADER -107
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_REMOVE_HTTP_HEADER` indicates that an HTTP
+ * header field is discarded.
+ */
+#define NGHTTP3_ERR_REMOVE_HTTP_HEADER -108
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING` indicates that HTTP
+ * messaging is malformed.
+ */
+#define NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING -109
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL` indicates that a fatal error is
+ * occurred during QPACK processing and it cannot be recoverable.
+ */
+#define NGHTTP3_ERR_QPACK_FATAL -111
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` indicates that a header
+ * field is too large to process.
+ */
+#define NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE -112
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` indicates that a stream is
+ * not found.
+ */
+#define NGHTTP3_ERR_STREAM_NOT_FOUND -114
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is
+ * closing state.
+ */
+#define NGHTTP3_ERR_CONN_CLOSING -116
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_DATA_OVERFLOW` indicates that the length
+ * of stream data is too long and causes overflow.
+ */
+#define NGHTTP3_ERR_STREAM_DATA_OVERFLOW -117
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` indicates that a
+ * QPACK decompression failed.
+ */
+#define NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED -402
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR` indicates that an
+ * error occurred while reading QPACK encoder stream.
+ */
+#define NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR -403
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` indicates that an
+ * error occurred while reading QPACK decoder stream.
+ */
+#define NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR -404
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_FRAME_UNEXPECTED` indicates that an
+ * unexpected HTTP/3 frame is received.
+ */
+#define NGHTTP3_ERR_H3_FRAME_UNEXPECTED -408
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_FRAME_ERROR` indicates that an HTTP/3 frame
+ * is malformed.
+ */
+#define NGHTTP3_ERR_H3_FRAME_ERROR -409
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_MISSING_SETTINGS` indicates that an HTTP/3
+ * SETTINGS frame is missing.
+ */
+#define NGHTTP3_ERR_H3_MISSING_SETTINGS -665
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_INTERNAL_ERROR` indicates an internal error.
+ */
+#define NGHTTP3_ERR_H3_INTERNAL_ERROR -667
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` indicates that a
+ * critical stream is closed.
+ */
+#define NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM -668
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR` indicates a general
+ * protocol error. This is typically a catch-all error.
+ */
+#define NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR -669
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_ID_ERROR` indicates that an ID related error
+ * occurred.
+ */
+#define NGHTTP3_ERR_H3_ID_ERROR -670
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_SETTINGS_ERROR` indicates that an HTTP/3
+ * SETTINGS frame is malformed.
+ */
+#define NGHTTP3_ERR_H3_SETTINGS_ERROR -671
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_STREAM_CREATION_ERROR` indicates that a
+ * remote endpoint attempts to create a new stream which is not
+ * allowed.
+ */
+#define NGHTTP3_ERR_H3_STREAM_CREATION_ERROR -672
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_FATAL` indicates that error codes less than
+ * this value is fatal error. When this error is returned, an
+ * endpoint should drop connection immediately.
+ */
+#define NGHTTP3_ERR_FATAL -900
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM` indicates out of memory.
+ */
+#define NGHTTP3_ERR_NOMEM -901
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` indicates that user defined
+ * callback function failed.
+ */
+#define NGHTTP3_ERR_CALLBACK_FAILURE -902
+
+/**
+ * @macrosection
+ *
+ * HTTP/3 application error code
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_NO_ERROR` is HTTP/3 application error code
+ * ``H3_NO_ERROR``.
+ */
+#define NGHTTP3_H3_NO_ERROR 0x0100
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_GENERAL_PROTOCOL_ERROR` is HTTP/3 application
+ * error code ``H3_GENERAL_PROTOCOL_ERROR``.
+ */
+#define NGHTTP3_H3_GENERAL_PROTOCOL_ERROR 0x0101
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_INTERNAL_ERROR` is HTTP/3 application error code
+ * ``H3_INTERNAL_ERROR``.
+ */
+#define NGHTTP3_H3_INTERNAL_ERROR 0x0102
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_STREAM_CREATION_ERROR` is HTTP/3 application
+ * error code ``H3_STREAM_CREATION_ERROR``.
+ */
+#define NGHTTP3_H3_STREAM_CREATION_ERROR 0x0103
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_CLOSED_CRITICAL_STREAM` is HTTP/3 application
+ * error code ``H3_CLOSED_CRITICAL_STREAM``.
+ */
+#define NGHTTP3_H3_CLOSED_CRITICAL_STREAM 0x0104
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_FRAME_UNEXPECTED` is HTTP/3 application error
+ * code ``H3_FRAME_UNEXPECTED``.
+ */
+#define NGHTTP3_H3_FRAME_UNEXPECTED 0x0105
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_FRAME_ERROR` is HTTP/3 application error code
+ * ``H3_FRAME_ERROR``.
+ */
+#define NGHTTP3_H3_FRAME_ERROR 0x0106
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_EXCESSIVE_LOAD` is HTTP/3 application error code
+ * ``H3_EXCESSIVE_LOAD``.
+ */
+#define NGHTTP3_H3_EXCESSIVE_LOAD 0x0107
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_ID_ERROR` is HTTP/3 application error code
+ * ``H3_ID_ERROR``.
+ */
+#define NGHTTP3_H3_ID_ERROR 0x0108
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_SETTINGS_ERROR` is HTTP/3 application error code
+ * ``H3_SETTINGS_ERROR``.
+ */
+#define NGHTTP3_H3_SETTINGS_ERROR 0x0109
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_MISSING_SETTINGS` is HTTP/3 application error
+ * code ``H3_MISSING_SETTINGS``.
+ */
+#define NGHTTP3_H3_MISSING_SETTINGS 0x010a
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_REJECTED` is HTTP/3 application error
+ * code ``H3_REQUEST_REJECTED``.
+ */
+#define NGHTTP3_H3_REQUEST_REJECTED 0x010b
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_CANCELLED` is HTTP/3 application error
+ * code ``H3_REQUEST_CANCELLED``.
+ */
+#define NGHTTP3_H3_REQUEST_CANCELLED 0x010c
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_INCOMPLETE` is HTTP/3 application error
+ * code ``H3_REQUEST_INCOMPLETE``.
+ */
+#define NGHTTP3_H3_REQUEST_INCOMPLETE 0x010d
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_MESSAGE_ERROR` is HTTP/3 application error code
+ * ``H3_MESSAGE_ERROR``.
+ */
+#define NGHTTP3_H3_MESSAGE_ERROR 0x010e
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_CONNECT_ERROR` is HTTP/3 application error code
+ * ``H3_CONNECT_ERROR``.
+ */
+#define NGHTTP3_H3_CONNECT_ERROR 0x010f
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_VERSION_FALLBACK` is HTTP/3 application error
+ * code ``H3_VERSION_FALLBACK``.
+ */
+#define NGHTTP3_H3_VERSION_FALLBACK 0x0110
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECOMPRESSION_FAILED` is HTTP/3 application
+ * error code ``QPACK_DECOMPRESSION_FAILED``.
+ */
+#define NGHTTP3_QPACK_DECOMPRESSION_FAILED 0x0200
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_ENCODER_STREAM_ERROR` is HTTP/3 application
+ * error code ``QPACK_ENCODER_STREAM_ERROR``.
+ */
+#define NGHTTP3_QPACK_ENCODER_STREAM_ERROR 0x0201
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODER_STREAM_ERROR` is HTTP/3 application
+ * error code ``QPACK_DECODER_STREAM_ERROR``.
+ */
+#define NGHTTP3_QPACK_DECODER_STREAM_ERROR 0x0202
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_malloc` is a custom memory allocator to replace
+ * :manpage:`malloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void *(*nghttp3_malloc)(size_t size, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_free` is a custom memory allocator to replace
+ * :manpage:`free(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void (*nghttp3_free)(void *ptr, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_calloc` is a custom memory allocator to replace
+ * :manpage:`calloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_realloc` is a custom memory allocator to replace
+ * :manpage:`realloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *user_data);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_mem` is a custom memory allocator functions and user
+ * defined pointer. The :member:`user_data` field is passed to each
+ * allocator function. This can be used, for example, to achieve
+ * per-session memory pool.
+ *
+ * In the following example code, ``my_malloc``, ``my_free``,
+ * ``my_calloc`` and ``my_realloc`` are the replacement of the
+ * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`,
+ * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively::
+ *
+ * void *my_malloc_cb(size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_malloc(size);
+ * }
+ *
+ * void my_free_cb(void *ptr, void *user_data) {
+ * (void)user_data;
+ * my_free(ptr);
+ * }
+ *
+ * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_calloc(nmemb, size);
+ * }
+ *
+ * void *my_realloc_cb(void *ptr, size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_realloc(ptr, size);
+ * }
+ *
+ * void conn_new() {
+ * nghttp3_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,
+ * my_realloc_cb};
+ *
+ * ...
+ * }
+ */
+typedef struct nghttp3_mem {
+ /**
+ * :member:`user_data` is an arbitrary user supplied data. This
+ * is passed to each allocator function.
+ */
+ void *user_data;
+ /**
+ * :member:`malloc` is a custom allocator function to replace
+ * :manpage:`malloc(3)`.
+ */
+ nghttp3_malloc malloc;
+ /**
+ * :member:`free` is a custom allocator function to replace
+ * :manpage:`free(3)`.
+ */
+ nghttp3_free free;
+ /**
+ * :member:`calloc` is a custom allocator function to replace
+ * :manpage:`calloc(3)`.
+ */
+ nghttp3_calloc calloc;
+ /**
+ * :member:`realloc` is a custom allocator function to replace
+ * :manpage:`realloc(3)`.
+ */
+ nghttp3_realloc realloc;
+} nghttp3_mem;
+
+/**
+ * @function
+ *
+ * `nghttp3_mem_default` returns the default memory allocator which
+ * uses malloc/calloc/realloc/free.
+ */
+NGHTTP3_EXTERN const nghttp3_mem *nghttp3_mem_default(void);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_vec` is ``struct iovec`` compatible structure to
+ * reference arbitrary array of bytes.
+ */
+typedef struct nghttp3_vec {
+ /**
+ * :member:`base` points to the data.
+ */
+ uint8_t *base;
+ /**
+ * :member:`len` is the number of bytes which the buffer pointed by
+ * base contains.
+ */
+ size_t len;
+} nghttp3_vec;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_rcbuf` is the object representing reference counted
+ * buffer. The details of this structure are intentionally hidden
+ * from the public API.
+ */
+typedef struct nghttp3_rcbuf nghttp3_rcbuf;
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_incref` increments the reference count of |rcbuf| by
+ * 1.
+ */
+NGHTTP3_EXTERN void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_decref` decrements the reference count of |rcbuf| by
+ * 1. If the reference count becomes zero, the object pointed by
+ * |rcbuf| will be freed. In this case, application must not use
+ * |rcbuf| again.
+ */
+NGHTTP3_EXTERN void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_get_buf` returns the underlying buffer managed by
+ * |rcbuf|.
+ */
+NGHTTP3_EXTERN nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_is_static` returns nonzero if the underlying buffer
+ * is statically allocated, and 0 otherwise. This can be useful for
+ * language bindings that wish to avoid creating duplicate strings for
+ * these buffers.
+ */
+NGHTTP3_EXTERN int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_buf` is the variable size buffer.
+ */
+typedef struct nghttp3_buf {
+ /**
+ * :member:`begin` points to the beginning of the buffer.
+ */
+ uint8_t *begin;
+ /**
+ * :member:`end` points to the one beyond of the last byte of the
+ * buffer
+ */
+ uint8_t *end;
+ /**
+ * :member:`pos` pointers to the start of data. Typically, this
+ * points to the point that next data should be read. Initially, it
+ * points to :member:`begin`.
+ */
+ uint8_t *pos;
+ /**
+ * :member:`last` points to the one beyond of the last data of the
+ * buffer. Typically, new data is written at this point.
+ * Initially, it points to :member:`begin`.
+ */
+ uint8_t *last;
+} nghttp3_buf;
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_init` initializes empty |buf|.
+ */
+NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_free` frees resources allocated for |buf| using |mem|
+ * as memory allocator. :member:`buf->begin <nghttp3_buf.begin>` must
+ * be a heap buffer allocated by |mem|.
+ */
+NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_left` returns the number of additional bytes which can
+ * be written to the underlying buffer. In other words, it returns
+ * :member:`buf->end <nghttp3_buf.end>` - :member:`buf->last
+ * <nghttp3_buf.last>`.
+ */
+NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_len` returns the number of bytes left to read. In
+ * other words, it returns :member:`buf->last <nghttp3_buf.last>` -
+ * :member:`buf->pos <nghttp3_buf.pos>`.
+ */
+NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_reset` sets :member:`buf->pos <nghttp3_buf.pos>` and
+ * :member:`buf->last <nghttp3_buf.last>` to :member:`buf->begin
+ * <nghttp3_buf.begin>`.
+ */
+NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
+
+/**
+ * @macrosection
+ *
+ * Flags for header field name/value pair
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set.
+ */
+#define NGHTTP3_NV_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NEVER_INDEX` indicates that this name/value
+ * pair must not be indexed. Other implementation calls this bit as
+ * "sensitive".
+ */
+#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NO_COPY_NAME` is set solely by application.
+ * If this flag is set, the library does not make a copy of header
+ * field name. This could improve performance.
+ */
+#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NO_COPY_VALUE` is set solely by
+ * application. If this flag is set, the library does not make a copy
+ * of header field value. This could improve performance.
+ */
+#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04u
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_nv` is the name/value pair, which mainly used to
+ * represent header fields.
+ */
+typedef struct nghttp3_nv {
+ /**
+ * :member:`name` is the header field name.
+ */
+ uint8_t *name;
+ /**
+ * :member:`value` is the header field value.
+ */
+ uint8_t *value;
+ /**
+ * :member:`namelen` is the length of the |name|, excluding
+ * terminating NULL.
+ */
+ size_t namelen;
+ /**
+ * :member:`valuelen` is the length of the |value|, excluding
+ * terminating NULL.
+ */
+ size_t valuelen;
+ /**
+ * :member:`flags` is bitwise OR of one or more of
+ * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
+ */
+ uint8_t flags;
+} nghttp3_nv;
+
+/* Generated by mkstatichdtbl.py */
+/**
+ * @enum
+ *
+ * :type:`nghttp3_qpack_token` defines HTTP header field name tokens
+ * to identify field name quickly. It appears in
+ * :member:`nghttp3_qpack_nv.token`.
+ */
+typedef enum nghttp3_qpack_token {
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__AUTHORITY` is a token for
+ * ``:authority``.
+ */
+ NGHTTP3_QPACK_TOKEN__AUTHORITY = 0,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__PATH` is a token for ``:path``.
+ */
+ NGHTTP3_QPACK_TOKEN__PATH = 8,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_AGE` is a token for ``age``.
+ */
+ NGHTTP3_QPACK_TOKEN_AGE = 43,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION` is a token for
+ * ``content-disposition``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION = 52,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH` is a token for
+ * ``content-length``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH = 55,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_COOKIE` is a token for ``cookie``.
+ */
+ NGHTTP3_QPACK_TOKEN_COOKIE = 68,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_DATE` is a token for ``date``.
+ */
+ NGHTTP3_QPACK_TOKEN_DATE = 69,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ETAG` is a token for ``etag``.
+ */
+ NGHTTP3_QPACK_TOKEN_ETAG = 71,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE` is a token for
+ * ``if-modified-since``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE = 74,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH` is a token for
+ * ``if-none-match``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH = 75,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LAST_MODIFIED` is a token for
+ * ``last-modified``.
+ */
+ NGHTTP3_QPACK_TOKEN_LAST_MODIFIED = 77,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LINK` is a token for ``link``.
+ */
+ NGHTTP3_QPACK_TOKEN_LINK = 78,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LOCATION` is a token for ``location``.
+ */
+ NGHTTP3_QPACK_TOKEN_LOCATION = 79,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_REFERER` is a token for ``referer``.
+ */
+ NGHTTP3_QPACK_TOKEN_REFERER = 83,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_SET_COOKIE` is a token for
+ * ``set-cookie``.
+ */
+ NGHTTP3_QPACK_TOKEN_SET_COOKIE = 85,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__METHOD` is a token for ``:method``.
+ */
+ NGHTTP3_QPACK_TOKEN__METHOD = 1,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__SCHEME` is a token for ``:scheme``.
+ */
+ NGHTTP3_QPACK_TOKEN__SCHEME = 9,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__STATUS` is a token for ``:status``.
+ */
+ NGHTTP3_QPACK_TOKEN__STATUS = 11,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT` is a token for ``accept``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT = 25,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING` is a token for
+ * ``accept-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING = 27,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES` is a token for
+ * ``accept-ranges``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES = 29,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS` is a
+ * token for ``access-control-allow-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS = 32,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN` is a
+ * token for ``access-control-allow-origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 38,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CACHE_CONTROL` is a token for
+ * ``cache-control``.
+ */
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL = 46,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING` is a token for
+ * ``content-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING = 53,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_TYPE` is a token for
+ * ``content-type``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE = 57,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_RANGE` is a token for ``range``.
+ */
+ NGHTTP3_QPACK_TOKEN_RANGE = 82,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY` is a token
+ * for ``strict-transport-security``.
+ */
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY = 86,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_VARY` is a token for ``vary``.
+ */
+ NGHTTP3_QPACK_TOKEN_VARY = 92,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS` is a token for
+ * ``x-content-type-options``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS = 94,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION` is a token for
+ * ``x-xss-protection``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION = 98,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE` is a token for
+ * ``accept-language``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE = 28,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS` is a
+ * token for ``access-control-allow-credentials``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS = 30,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS` is a
+ * token for ``access-control-allow-methods``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS = 35,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS` is a
+ * token for ``access-control-expose-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS = 39,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS` is a
+ * token for ``access-control-request-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS = 40,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD` is a
+ * token for ``access-control-request-method``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD = 41,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ALT_SVC` is a token for ``alt-svc``.
+ */
+ NGHTTP3_QPACK_TOKEN_ALT_SVC = 44,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_AUTHORIZATION` is a token for
+ * ``authorization``.
+ */
+ NGHTTP3_QPACK_TOKEN_AUTHORIZATION = 45,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY` is a token
+ * for ``content-security-policy``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY = 56,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_EARLY_DATA` is a token for
+ * ``early-data``.
+ */
+ NGHTTP3_QPACK_TOKEN_EARLY_DATA = 70,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_EXPECT_CT` is a token for
+ * ``expect-ct``.
+ */
+ NGHTTP3_QPACK_TOKEN_EXPECT_CT = 72,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_FORWARDED` is a token for
+ * ``forwarded``.
+ */
+ NGHTTP3_QPACK_TOKEN_FORWARDED = 73,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_RANGE` is a token for ``if-range``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_RANGE = 76,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ORIGIN` is a token for ``origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_ORIGIN = 80,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PURPOSE` is a token for ``purpose``.
+ */
+ NGHTTP3_QPACK_TOKEN_PURPOSE = 81,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_SERVER` is a token for ``server``.
+ */
+ NGHTTP3_QPACK_TOKEN_SERVER = 84,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN` is a token for
+ * ``timing-allow-origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN = 89,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS` is a token
+ * for ``upgrade-insecure-requests``.
+ */
+ NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS = 90,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_USER_AGENT` is a token for
+ * ``user-agent``.
+ */
+ NGHTTP3_QPACK_TOKEN_USER_AGENT = 91,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR` is a token for
+ * ``x-forwarded-for``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR = 95,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS` is a token for
+ * ``x-frame-options``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS = 96,
+
+ /* Additional header fields for HTTP messaging validation */
+
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_HOST` is a token for ``host``.
+ */
+ NGHTTP3_QPACK_TOKEN_HOST = 1000,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONNECTION` is a token for
+ * ``connection``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONNECTION,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_KEEP_ALIVE` is a token for
+ * ``keep-alive``.
+ */
+ NGHTTP3_QPACK_TOKEN_KEEP_ALIVE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION` is a token for
+ * ``proxy-connection``.
+ */
+ NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING` is a token for
+ * ``transfer-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE` is a token for ``upgrade``.
+ */
+ NGHTTP3_QPACK_TOKEN_UPGRADE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TE` is a token for ``te``.
+ */
+ NGHTTP3_QPACK_TOKEN_TE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__PROTOCOL` is a token for
+ * ``:protocol``.
+ */
+ NGHTTP3_QPACK_TOKEN__PROTOCOL,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PRIORITY` is a token for ``priority``.
+ */
+ NGHTTP3_QPACK_TOKEN_PRIORITY
+} nghttp3_qpack_token;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_nv` represents header field name/value pair
+ * just like :type:`nghttp3_nv`. It is an extended version of
+ * :type:`nghttp3_nv` and has reference counted buffers and tokens
+ * which might be useful for applications.
+ */
+typedef struct nghttp3_qpack_nv {
+ /**
+ * :member:`name` is the buffer containing header field name.
+ * NULL-termination is guaranteed.
+ */
+ nghttp3_rcbuf *name;
+ /**
+ * :member:`value` is the buffer containing header field value.
+ * NULL-termination is guaranteed.
+ */
+ nghttp3_rcbuf *value;
+ /**
+ * :member:`token` is :type:`nghttp3_qpack_token` value of
+ * :member:`name`. It could be -1 if we have no token for that
+ * header field name.
+ */
+ int32_t token;
+ /**
+ * :member:`flags` is a bitwise OR of one or more of
+ * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
+ */
+ uint8_t flags;
+} nghttp3_qpack_nv;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_encoder` is QPACK encoder.
+ */
+typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder;
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder|
+ * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper
+ * bound of the dynamic table capacity. |mem| is a memory allocator.
+ * This function allocates memory for :type:`nghttp3_qpack_encoder`
+ * itself and assigns its pointer to |*pencoder| if it succeeds.
+ *
+ * The maximum dynamic table capacity is still 0. In order to change
+ * the maximum dynamic table capacity, call
+ * `nghttp3_qpack_encoder_set_max_dtable_capacity`.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_del` frees memory allocated for |encoder|.
+ * This function frees memory pointed by |encoder| itself.
+ */
+NGHTTP3_EXTERN void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_encode` encodes the list of header fields
+ * |nva|. |nvlen| is the length of |nva|. |stream_id| is the
+ * identifier of the stream which this header fields belong to. This
+ * function writes header block prefix, encoded header fields, and
+ * encoder stream to |pbuf|, |rbuf|, and |ebuf| respectively. The
+ * :member:`nghttp3_buf.last` will be adjusted when data is written.
+ * An application should write |pbuf| and |rbuf| to the request stream
+ * in this order.
+ *
+ * The buffer pointed by |pbuf|, |rbuf|, and |ebuf| can be empty
+ * buffer. It is fine to pass a buffer initialized by
+ * `nghttp3_buf_init(buf) <nghttp3_buf_init>`. This function
+ * allocates memory for these buffers as necessary. In particular, it
+ * frees and expands buffer if the current capacity of buffer is not
+ * enough. If :member:`nghttp3_buf.begin` of any buffer is not NULL,
+ * it must be allocated by the same memory allocator passed to
+ * `nghttp3_qpack_encoder_new()`.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |encoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_encoder_encode(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, nghttp3_buf *rbuf,
+ nghttp3_buf *ebuf, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_read_decoder` reads decoder stream. The
+ * buffer pointed by |src| of length |srclen| contains decoder stream.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |encoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM`
+ * |encoder| is unable to process input because it is malformed.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder(
+ nghttp3_qpack_encoder *encoder, const uint8_t *src, size_t srclen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_set_max_dtable_capacity` sets max dynamic
+ * table capacity to |max_dtable_capacity|. If |max_dtable_capacity| is
+ * larger than ``hard_max_dtable_capacity`` parameter of
+ * `nghttp3_qpack_encoder_new`, it is truncated to the latter.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_encoder_set_max_dtable_capacity(nghttp3_qpack_encoder *encoder,
+ size_t max_dtable_capacity);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_set_max_blocked_streams` sets the number of
+ * streams which can be blocked to |max_blocked_streams|.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_encoder_set_max_blocked_streams(nghttp3_qpack_encoder *encoder,
+ size_t max_blocked_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_ack_everything` tells |encoder| that all
+ * encoded header blocks are acknowledged. This function is provided
+ * for debugging purpose only. In HTTP/3, |encoder| knows this by
+ * reading decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_get_num_blocked_streams` returns the number
+ * of streams which are potentially blocked at decoder side.
+ */
+NGHTTP3_EXTERN size_t
+nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_stream_context` is a decoder context for an
+ * individual stream. Its state is per header block. In order to
+ * reuse this object for another header block, call
+ * `nghttp3_qpack_stream_context_reset`.
+ */
+typedef struct nghttp3_qpack_stream_context nghttp3_qpack_stream_context;
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_new` initializes stream context.
+ * |psctx| must be non-NULL pointer. |stream_id| is stream ID. |mem|
+ * is a memory allocator. This function allocates memory for
+ * :type:`nghttp3_qpack_stream_context` itself and assigns its pointer
+ * to |*psctx| if it succeeds.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx,
+ int64_t stream_id, const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_del` frees memory allocated for
+ * |sctx|. This function frees memory pointed by |sctx| itself.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_get_ricnt` returns required insert
+ * count.
+ */
+NGHTTP3_EXTERN uint64_t
+nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_reset` resets the state of |sctx|.
+ * Then it can be reused for an another header block in the same
+ * stream.
+ */
+NGHTTP3_EXTERN
+void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_decoder` is QPACK decoder.
+ */
+typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder;
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder|
+ * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper
+ * bound of the dynamic table capacity. |max_blocked_streams| is the
+ * maximum number of streams which can be blocked. |mem| is a memory
+ * allocator. This function allocates memory for
+ * :type:`nghttp3_qpack_decoder` itself and assigns its pointer to
+ * |*pdecoder| if it succeeds.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_del` frees memory allocated for |decoder|.
+ * This function frees memory pointed by |decoder| itself.
+ */
+NGHTTP3_EXTERN void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_read_encoder` reads encoder stream. The
+ * buffer pointed by |src| of length |srclen| contains encoder stream.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |decoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM`
+ * Could not interpret encoder stream instruction.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_encoder(
+ nghttp3_qpack_decoder *decoder, const uint8_t *src, size_t srclen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_get_icnt` returns insert count.
+ */
+NGHTTP3_EXTERN uint64_t
+nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
+
+/**
+ * @macrosection
+ *
+ * Flags for QPACK decoder
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header
+ * field is successfully decoded.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header
+ * fields have been decoded.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding
+ * has been blocked.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04u
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_read_request` reads request stream. The
+ * request stream is given as the buffer pointed by |src| of length
+ * |srclen|. |sctx| is the stream context and it must be created by
+ * `nghttp3_qpack_stream_context_new()`. |*pflags| must be non-NULL
+ * pointer. |nv| must be non-NULL pointer.
+ *
+ * If this function succeeds, it assigns flags to |*pflags|. If
+ * |*pflags| has :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a
+ * decoded header field is assigned to |nv|. If |*pflags| has
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, all header fields
+ * have been successfully decoded. If |*pflags| has
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked
+ * due to required insert count.
+ *
+ * When a header field is decoded, an application receives it in |nv|.
+ * :member:`nv->name <nghttp3_qpack_nv.name>` and :member:`nv->value
+ * <nghttp3_qpack_nv.value>` are reference counted buffer, and their
+ * reference counts are already incremented for application use.
+ * Therefore, when application finishes processing the header field,
+ * it must call `nghttp3_rcbuf_decref(nv->name)
+ * <nghttp3_rcbuf_decref>` and `nghttp3_rcbuf_decref(nv->value)
+ * <nghttp3_rcbuf_decref>` or memory leak might occur. These
+ * :type:`nghttp3_rcbuf` objects hold the pointer to
+ * :type:`nghttp3_mem` that is passed to `nghttp3_qpack_decoder_new`
+ * (or either `nghttp3_conn_client_new` or `nghttp3_conn_server_new`
+ * if it is used indirectly). As long as these objects are alive, the
+ * pointed :type:`nghttp3_mem` object must be available. Otherwise,
+ * `nghttp3_rcbuf_decref` will cause undefined behavior.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |decoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED`
+ * Could not interpret header block instruction.
+ * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE`
+ * Header field is too large.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_request(
+ nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv, uint8_t *pflags, const uint8_t *src, size_t srclen,
+ int fin);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_write_decoder` writes decoder stream into
+ * |dbuf|.
+ *
+ * The caller must ensure that `nghttp3_buf_left(dbuf)
+ * <nghttp3_buf_left>` >=
+ * `nghttp3_qpack_decoder_get_decoder_streamlen(decoder)
+ * <nghttp3_qpack_decoder_get_decoder_streamlen>`.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
+ nghttp3_buf *dbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_get_decoder_streamlen` returns the length of
+ * decoder stream.
+ */
+NGHTTP3_EXTERN size_t
+nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_cancel_stream` cancels header decoding for
+ * stream denoted by |stream_id|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * Decoder stream overflow.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_set_max_dtable_capacity` sets
+ * |max_dtable_capacity| as maximum dynamic table size.
+ * |max_dtable_capacity| must be equal to or smaller than
+ * ``hard_max_dtable_capacity`` parameter of
+ * `nghttp3_qpack_decoder_new`. Normally, the maximum capacity is
+ * communicated in encoder stream. This function is provided for
+ * debugging and testing purpose.
+ *
+ * This function returns 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * |max_dtable_capacity| exceeds the upper bound of the dynamic
+ * table capacity.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_decoder_set_max_dtable_capacity(nghttp3_qpack_decoder *decoder,
+ size_t max_dtable_capacity);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_set_max_concurrent_streams` tells |decoder|
+ * the maximum number of concurrent streams that a remote endpoint can
+ * open, including both bidirectional and unidirectional streams which
+ * potentially receive QPACK encoded HEADERS frame. This value is
+ * used as a hint to limit the length of decoder stream.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_decoder_set_max_concurrent_streams(nghttp3_qpack_decoder *decoder,
+ size_t max_concurrent_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_strerror` returns textual representation of |liberr|.
+ */
+NGHTTP3_EXTERN const char *nghttp3_strerror(int liberr);
+
+/**
+ * @function
+ *
+ * `nghttp3_err_infer_quic_app_error_code` returns a QUIC application
+ * error code which corresponds to |liberr|.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_debug_vprintf_callback` is a callback function
+ * invoked when the library outputs debug logging. The function is
+ * called with arguments suitable for :manpage:`vfprintf(3)`.
+ *
+ * The debug output is only enabled if the library is built with
+ * :macro:`DEBUGBUILD` macro defined.
+ */
+typedef void (*nghttp3_debug_vprintf_callback)(const char *format,
+ va_list args);
+
+/**
+ * @function
+ *
+ * `nghttp3_set_debug_vprintf_callback` sets a debug output callback
+ * called by the library when built with :macro:`DEBUGBUILD` macro
+ * defined. If this option is not used, debug log is written into
+ * standard error output.
+ *
+ * For builds without :macro:`DEBUGBUILD` macro defined, this function
+ * is noop.
+ *
+ * Note that building with :macro:`DEBUGBUILD` may cause significant
+ * performance penalty to libnghttp3 because of extra processing. It
+ * should be used for debugging purpose only.
+ *
+ * .. Warning::
+ *
+ * Building with :macro:`DEBUGBUILD` may cause significant
+ * performance penalty to libnghttp3 because of extra processing.
+ * It should be used for debugging purpose only. We write this two
+ * times because this is important.
+ */
+NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback);
+
+/**
+ * @macrosection
+ *
+ * Shutdown related constants
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` specifies stream id sent
+ * by a server when it initiates graceful shutdown of the connection
+ * via `nghttp3_conn_submit_shutdown_notice`.
+ */
+#define NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID ((1ull << 62) - 4)
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID` specifies push id sent
+ * by a client when it initiates graceful shutdown of the connection
+ * via `nghttp3_conn_submit_shutdown_notice`.
+ */
+#define NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID ((1ull << 62) - 1)
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_conn` represents a single HTTP/3 connection.
+ */
+typedef struct nghttp3_conn nghttp3_conn;
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_acked_stream_data` is a callback function which is
+ * invoked when data sent on stream denoted by |stream_id| supplied
+ * from application is acknowledged by remote endpoint. The number of
+ * bytes acknowledged is given in |datalen|.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_conn_stream_close` is a callback function which is
+ * invoked when a stream identified by |stream_id| is closed.
+ * |app_error_code| indicates the reason of this closure.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_stream_close)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_recv_data` is a callback function which is invoked
+ * when a part of request or response body on stream identified by
+ * |stream_id| is received. |data| points to the received data and
+ * its length is |datalen|.
+ *
+ * The application is responsible for increasing flow control credit
+ * by |datalen| bytes.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_recv_data)(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *data, size_t datalen,
+ void *conn_user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_deferred_consume` is a callback function which is
+ * invoked when the library consumed |consumed| bytes for a stream
+ * identified by |stream_id|. This callback is used to notify the
+ * consumed bytes for stream blocked by QPACK decoder. The
+ * application is responsible for increasing flow control credit by
+ * |consumed| bytes.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_deferred_consume)(nghttp3_conn *conn, int64_t stream_id,
+ size_t consumed, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_begin_headers` is a callback function which is
+ * invoked when an incoming header block section is started on a
+ * stream denoted by |stream_id|. Each header field is passed to
+ * application by :type:`nghttp3_recv_header` callback. And then
+ * :type:`nghttp3_end_headers` is called when a whole header block is
+ * processed.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_recv_header` is a callback function which is invoked
+ * when a header field is received on a stream denoted by |stream_id|.
+ * |name| contains a field name and |value| contains a field value.
+ * |token| is one of token defined in :type:`nghttp3_qpack_token` or
+ * -1 if no token is defined for |name|. |flags| is bitwise OR of
+ * zero or more of :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
+ *
+ * The buffers for |name| and |value| are reference counted. If
+ * application needs to keep them, increment the reference count with
+ * `nghttp3_rcbuf_incref`. When they are no longer used, call
+ * `nghttp3_rcbuf_decref`.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id,
+ int32_t token, nghttp3_rcbuf *name,
+ nghttp3_rcbuf *value, uint8_t flags,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_end_headers` is a callback function which is invoked
+ * when an incoming header block has ended.
+ *
+ * If the stream ends with this header block, |fin| is set to nonzero.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id,
+ int fin, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_end_stream` is a callback function which is invoked
+ * when the receiving side of stream is closed. For server, this
+ * callback function is invoked when HTTP request is received
+ * completely. For client, this callback function is invoked when
+ * HTTP response is received completely.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id,
+ void *conn_user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_stop_sending` is a callback function which is
+ * invoked when the library asks application to send STOP_SENDING to
+ * the stream identified by |stream_id|. |app_error_code| indicates
+ * the reason for this action.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_stop_sending)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_reset_stream` is a callback function which is
+ * invoked when the library asks application to reset stream
+ * identified by |stream_id|. |app_error_code| indicates the reason
+ * for this action.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_shutdown` is a callback function which is invoked
+ * when a shutdown is initiated by the remote endpoint. For client,
+ * |id| contains a stream id of a client initiated stream, for server,
+ * it contains a push id. All client streams with stream id or pushes
+ * with push id equal to or larger than |id| are guaranteed to not be
+ * processed by the remote endpoint.
+ *
+ * Parameter |id| for client can contain a special value
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` and for server it can
+ * contain special value
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID`. These values signal
+ * request for graceful shutdown of the connection, triggered by
+ * remote endpoint's invocation of
+ * `nghttp3_conn_submit_shutdown_notice`.
+ *
+ * It is possible that this callback is invoked multiple times on a
+ * single connection, however the |id| can only stay the same or
+ * decrease, never increase.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_shutdown)(nghttp3_conn *conn, int64_t id,
+ void *conn_user_data);
+
+#define NGHTTP3_CALLBACKS_VERSION_V1 1
+#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_callbacks` holds a set of callback functions.
+ */
+typedef struct nghttp3_callbacks {
+ /**
+ * :member:`acked_stream_data` is a callback function which is
+ * invoked when data sent on a particular stream have been
+ * acknowledged by a remote endpoint.
+ */
+ nghttp3_acked_stream_data acked_stream_data;
+ /**
+ * :member:`stream_close` is a callback function which is invoked
+ * when a particular stream has closed.
+ */
+ nghttp3_stream_close stream_close;
+ /**
+ * :member:`recv_data` is a callback function which is invoked when
+ * stream data is received.
+ */
+ nghttp3_recv_data recv_data;
+ /**
+ * :member:`deferred_consume` is a callback function which is
+ * invoked when the library consumed data for a particular stream
+ * which had been blocked for synchronization between streams.
+ */
+ nghttp3_deferred_consume deferred_consume;
+ /**
+ * :member:`begin_headers` is a callback function which is invoked
+ * when a header block has started on a particular stream.
+ */
+ nghttp3_begin_headers begin_headers;
+ /**
+ * :member:`recv_header` is a callback function which is invoked
+ * when a single header field is received on a particular stream.
+ */
+ nghttp3_recv_header recv_header;
+ /**
+ * :member:`end_headers` is a callback function which is invoked
+ * when a header block has ended on a particular stream.
+ */
+ nghttp3_end_headers end_headers;
+ /**
+ * :member:`begin_trailers` is a callback function which is invoked
+ * when a trailer block has started on a particular stream.
+ */
+ nghttp3_begin_headers begin_trailers;
+ /**
+ * :member:`recv_trailer` is a callback function which is invoked
+ * when a single trailer field is received on a particular stream.
+ */
+ nghttp3_recv_header recv_trailer;
+ /**
+ * :member:`end_trailers` is a callback function which is invoked
+ * when a trailer block has ended on a particular stream.
+ */
+ nghttp3_end_headers end_trailers;
+ /**
+ * :member:`stop_sending` is a callback function which is invoked
+ * when the library asks application to send STOP_SENDING to a
+ * particular stream.
+ */
+ nghttp3_stop_sending stop_sending;
+ /**
+ * :member:`end_stream` is a callback function which is invoked when
+ * a receiving side of stream has been closed.
+ */
+ nghttp3_end_stream end_stream;
+ /**
+ * :member:`reset_stream` is a callback function which is invoked
+ * when the library asks application to reset stream (by sending
+ * RESET_STREAM).
+ */
+ nghttp3_reset_stream reset_stream;
+ /**
+ * :member:`shutdown` is a callback function which is invoked when
+ * the remote endpoint has signalled initiation of connection shutdown.
+ */
+ nghttp3_shutdown shutdown;
+} nghttp3_callbacks;
+
+#define NGHTTP3_SETTINGS_VERSION_V1 1
+#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_settings` defines HTTP/3 settings.
+ */
+typedef struct nghttp3_settings {
+ /**
+ * :member:`max_field_section_size` specifies the maximum header
+ * section (block) size.
+ */
+ uint64_t max_field_section_size;
+ /**
+ * :member:`qpack_max_dtable_capacity` is the maximum size of QPACK
+ * dynamic table.
+ */
+ size_t qpack_max_dtable_capacity;
+ /**
+ * :member:`qpack_encoder_max_dtable_capacity` is the upper bound of
+ * QPACK dynamic table capacity that the QPACK encoder is willing to
+ * use. The effective maximum dynamic table capacity is the minimum
+ * of this field and the value of the received
+ * SETTINGS_QPACK_MAX_TABLE_CAPACITY. If this field is set to 0,
+ * the encoder does not use the dynamic table.
+ */
+ size_t qpack_encoder_max_dtable_capacity;
+ /**
+ * :member:`qpack_blocked_streams` is the maximum number of streams
+ * which can be blocked while they are being decoded.
+ */
+ size_t qpack_blocked_streams;
+ /**
+ * :member:`enable_connect_protocol`, if set to nonzero, enables
+ * Extended CONNECT Method (see
+ * https://datatracker.ietf.org/doc/html/rfc9220). Client ignores
+ * this field.
+ */
+ int enable_connect_protocol;
+} nghttp3_settings;
+
+/**
+ * @function
+ *
+ * `nghttp3_settings_default` fills |settings| with the default
+ * values.
+ *
+ * - :member:`max_field_section_size
+ * <nghttp3_settings.max_field_section_size>` = :expr:`((1ull << 62) - 1)`
+ * - :member:`qpack_max_dtable_capacity
+ * <nghttp3_settings.qpack_max_dtable_capacity>` = 0
+ * - :member:`qpack_encoder_max_dtable_capacity
+ * <nghttp3_settings.qpack_encoder_max_dtable_capacity>` = 4096
+ * - :member:`qpack_blocked_streams
+ * <nghttp3_settings.qpack_blocked_streams>` = 0
+ * - :member:`enable_connect_protocol
+ * <nghttp3_settings.enable_connect_protocol>` = 0
+ */
+NGHTTP3_EXTERN void
+nghttp3_settings_default_versioned(int settings_version,
+ nghttp3_settings *settings);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_client_new` creates :type:`nghttp3_conn` and
+ * initializes it for client use. The pointer to the object is stored
+ * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned
+ * by `nghttp3_mem_default` is used.
+ */
+NGHTTP3_EXTERN int
+nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *conn_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_server_new` creates :type:`nghttp3_conn` and
+ * initializes it for server use. The pointer to the object is stored
+ * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned
+ * by `nghttp3_mem_default` is used.
+ */
+NGHTTP3_EXTERN int
+nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *conn_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_del` frees resources allocated for |conn|.
+ */
+NGHTTP3_EXTERN void nghttp3_conn_del(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_bind_control_stream` binds stream denoted by
+ * |stream_id| to outgoing unidirectional control stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE`
+ * Control stream has already corresponding stream ID.
+ *
+ * TBD
+ */
+NGHTTP3_EXTERN int nghttp3_conn_bind_control_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_bind_qpack_streams` binds stream denoted by
+ * |qenc_stream_id| to outgoing QPACK encoder stream and stream
+ * denoted by |qdec_stream_id| to outgoing QPACK encoder stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE`
+ * QPACK encoder/decoder stream have already corresponding stream
+ * IDs.
+ *
+ * TBD
+ */
+NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn,
+ int64_t qenc_stream_id,
+ int64_t qdec_stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_read_stream` reads data |src| of length |srclen| on
+ * stream identified by |stream_id|. It returns the number of bytes
+ * consumed. The "consumed" means that application can increase flow
+ * control credit (both stream and connection) of underlying QUIC
+ * connection by that amount. It does not include the amount of data
+ * carried by DATA frame which contains application data (excluding
+ * any control or QPACK unidirectional streams) . See
+ * :type:`nghttp3_recv_data` to handle those bytes. If |fin| is
+ * nonzero, this is the last data from remote endpoint in this stream.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn,
+ int64_t stream_id,
+ const uint8_t *src,
+ size_t srclen, int fin);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_writev_stream` stores stream data to send to |vec| of
+ * length |veccnt| and returns the number of nghttp3_vec object in
+ * which it stored data. It stores stream ID to |*pstream_id|. An
+ * application has to call `nghttp3_conn_add_write_offset` to inform
+ * |conn| of the actual number of bytes that underlying QUIC stack
+ * accepted. |*pfin| will be nonzero if this is the last data to
+ * send. If there is no stream to write data or send fin, this
+ * function returns 0, and -1 is assigned to |*pstream_id|. This
+ * function may return 0 and |*pstream_id| is not -1 and |*pfin| is
+ * nonzero. It means 0 length data to |*pstream_id| and it is the
+ * last data to the stream. They must be passed to QUIC stack, and
+ * they are accepted, the application has to call
+ * `nghttp3_conn_add_write_offset`.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
+ int64_t *pstream_id,
+ int *pfin,
+ nghttp3_vec *vec,
+ size_t veccnt);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_add_write_offset` tells |conn| the number of bytes
+ * |n| for stream denoted by |stream_id| QUIC stack accepted.
+ *
+ * If stream has no data to send but just sends fin (closing the write
+ * side of a stream), the number of bytes sent is 0. It is important
+ * to call this function even if |n| is 0 in this case. It is safe to
+ * call this function if |n| is 0.
+ *
+ * `nghttp3_conn_writev_stream` must be called before calling this
+ * function to get data to send, and those data must be fed into QUIC
+ * stack.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_add_write_offset(nghttp3_conn *conn,
+ int64_t stream_id, size_t n);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_add_ack_offset` tells |conn| the number of bytes |n|
+ * for stream denoted by |stream_id| QUIC stack has acknowledged.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn,
+ int64_t stream_id, uint64_t n);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_block_stream` tells the library that stream
+ * identified by |stream_id| is blocked due to QUIC flow control.
+ */
+NGHTTP3_EXTERN void nghttp3_conn_block_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_unblock_stream` tells the library that stream
+ * identified by |stream_id| which was blocked by QUIC flow control is
+ * unblocked.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_is_stream_writable` returns nonzero if a stream
+ * identified by |stream_id| is writable. It is not writable if:
+ *
+ * - the stream does not exist; or,
+ * - the stream is closed (e.g., `nghttp3_conn_close_stream` is
+ * called); or,
+ * - the stream is QUIC flow control blocked (e.g.,
+ * `nghttp3_conn_block_stream` is called); or,
+ * - the stream is input data blocked (e.g.,
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK` is returned from
+ * :type:`nghttp3_read_data_callback`); or,
+ * - the stream is half-closed local (e.g.,
+ * `nghttp3_conn_shutdown_stream_write` is called).
+ */
+NGHTTP3_EXTERN int nghttp3_conn_is_stream_writable(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_shutdown_stream_write` tells the library that any
+ * further write operation to stream identified by |stream_id| is
+ * prohibited. This works like `nghttp3_conn_block_stream`, but it
+ * cannot be unblocked by `nghttp3_conn_unblock_stream`.
+ */
+NGHTTP3_EXTERN void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_shutdown_stream_read` tells the library that
+ * read-side of stream denoted by |stream_id| is abruptly closed and
+ * any further incoming data and pending stream data should be
+ * discarded.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_resume_stream` resumes stream identified by
+ * |stream_id| which was previously unable to provide data.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_close_stream` closes stream identified by
+ * |stream_id|. |app_error_code| is the reason of the closure.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM`
+ * A critical stream is closed.
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn,
+ int64_t stream_id,
+ uint64_t app_error_code);
+
+/**
+ * @macrosection
+ *
+ * Data flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set.
+ */
+#define NGHTTP3_DATA_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DATA_FLAG_EOF` indicates that all request or
+ * response body has been provided to the library. It also indicates
+ * that sending side of stream is closed unless
+ * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same time.
+ */
+#define NGHTTP3_DATA_FLAG_EOF 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` indicates that sending
+ * side of stream is not closed even if :macro:`NGHTTP3_DATA_FLAG_EOF`
+ * is set. Usually this flag is used to send trailer fields with
+ * `nghttp3_conn_submit_trailers()`. If
+ * `nghttp3_conn_submit_trailers()` has been called, regardless of
+ * this flag, the submitted trailer fields are sent.
+ */
+#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02u
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_max_client_streams_bidi` tells |conn| the
+ * cumulative number of bidirectional streams that client can open.
+ */
+NGHTTP3_EXTERN void
+nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn,
+ uint64_t max_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_max_concurrent_streams` tells |conn| the maximum
+ * number of concurrent streams that a remote endpoint can open,
+ * including both bidirectional and unidirectional streams which
+ * potentially receive QPACK encoded HEADERS frame. This value is
+ * used as a hint to limit the internal resource consumption.
+ */
+NGHTTP3_EXTERN void
+nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn,
+ size_t max_concurrent_streams);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_read_data_callback` is a callback function invoked
+ * when the library asks an application to provide stream data for a
+ * stream denoted by |stream_id|.
+ *
+ * The library provides |vec| of length |veccnt| to the application.
+ * The application should fill data and its length to |vec|. It has
+ * to return the number of the filled objects. The application must
+ * retain data until they are safe to free. It is notified by
+ * :type:`nghttp3_acked_stream_data` callback.
+ *
+ * If this is the last data to send (or there is no data to send
+ * because all data have been sent already), set
+ * :macro:`NGHTTP3_DATA_FLAG_EOF` to |*pflags|.
+ *
+ * If the application is unable to provide data temporarily, return
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK`. When it is ready to provide
+ * data, call `nghttp3_conn_resume_stream()`.
+ *
+ * The callback should return the number of objects in |vec| that the
+ * application filled if it succeeds, or
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ *
+ * TODO Add NGHTTP3_ERR_TEMPORAL_CALLBACK_FAILURE to reset just this
+ * stream.
+ */
+typedef nghttp3_ssize (*nghttp3_read_data_callback)(
+ nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *conn_user_data, void *stream_user_data);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_data_reader` specifies the way how to generate
+ * request or response body.
+ */
+typedef struct nghttp3_data_reader {
+ /**
+ * :member:`read_data` is a callback function to generate body.
+ */
+ nghttp3_read_data_callback read_data;
+} nghttp3_data_reader;
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_request` submits HTTP request header fields
+ * and body on the stream identified by |stream_id|. |stream_id| must
+ * be a client initiated bidirectional stream. Only client can submit
+ * HTTP request. |nva| of length |nvlen| specifies HTTP request
+ * header fields. |dr| specifies a request body. If there is no
+ * request body, specify NULL. If |dr| is NULL, it implies the end of
+ * stream. |stream_user_data| is an opaque pointer attached to the
+ * stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_request(
+ nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr, void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_info` submits HTTP non-final response header
+ * fields on the stream identified by |stream_id|. |nva| of length
+ * |nvlen| specifies HTTP response header fields.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_info(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_response` submits HTTP response header fields
+ * and body on the stream identified by |stream_id|. |nva| of length
+ * |nvlen| specifies HTTP response header fields. |dr| specifies a
+ * response body. If there is no response body, specify NULL. If
+ * |dr| is NULL, it implies the end of stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_response(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen,
+ const nghttp3_data_reader *dr);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_trailers` submits HTTP trailer fields on the
+ * stream identified by |stream_id|. |nva| of length |nvlen|
+ * specifies HTTP trailer fields. Calling this function implies the
+ * end of stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_shutdown_notice` notifies the other endpoint
+ * to stop creating new stream. After a couple of RTTs later, call
+ * `nghttp3_conn_shutdown` to start graceful shutdown.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_shutdown` starts graceful shutdown. It should be
+ * called after `nghttp3_conn_submit_shutdown_notice` and a couple of
+ * RTT. After calling this function, the local endpoint starts
+ * rejecting new incoming streams. The existing streams are processed
+ * normally.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_stream_user_data` sets |stream_user_data| to the
+ * stream identified by |stream_id|.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn,
+ int64_t stream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_get_frame_payload_left` returns the number of bytes
+ * left to read current frame payload for a stream denoted by
+ * |stream_id|. If no such stream is found, it returns 0.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @macrosection
+ *
+ * HTTP stream priority flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level.
+ */
+#define NGHTTP3_DEFAULT_URGENCY 3
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_HIGH` is the highest urgency level.
+ */
+#define NGHTTP3_URGENCY_HIGH 0
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_LOW` is the lowest urgency level.
+ */
+#define NGHTTP3_URGENCY_LOW 7
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_LEVELS` is the number of urgency levels.
+ */
+#define NGHTTP3_URGENCY_LEVELS (NGHTTP3_URGENCY_LOW + 1)
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_pri` represents HTTP priority.
+ */
+typedef struct nghttp3_pri {
+ /**
+ * :member:`urgency` is the urgency of a stream, it must be in
+ * [:macro:`NGHTTP3_URGENCY_HIGH`, :macro:`NGHTTP3_URGENCY_LOW`],
+ * inclusive, and 0 is the highest urgency.
+ */
+ uint32_t urgency;
+ /**
+ * :member:`inc` indicates that a content can be processed
+ * incrementally or not. If inc is 0, it cannot be processed
+ * incrementally. If inc is 1, it can be processed incrementally.
+ * Other value is not permitted.
+ */
+ int inc;
+} nghttp3_pri;
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_get_stream_priority` stores stream priority of a
+ * stream denoted by |stream_id| into |*dest|. |stream_id| must
+ * identify client initiated bidirectional stream. Only server can
+ * use this function.
+ *
+ * This function must not be called if |conn| is initialized as
+ * client.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn,
+ nghttp3_pri *dest,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_stream_priority` updates priority of a stream
+ * denoted by |stream_id| with the value pointed by |pri|.
+ * |stream_id| must identify client initiated bidirectional stream.
+ *
+ * Both client and server can update stream priority with this
+ * function.
+ *
+ * If server updates stream priority with this function, it completely
+ * overrides stream priority set by client and the attempts to update
+ * priority by client are ignored.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * |stream_id| is not a client initiated bidirectional stream ID.
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_set_stream_priority(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_pri *pri);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_is_remote_qpack_encoder_stream` returns nonzero if a
+ * stream denoted by |stream_id| is QPACK encoder stream of a remote
+ * endpoint.
+ */
+NGHTTP3_EXTERN int
+nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt|
+ * elements.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt);
+
+/**
+ * @function
+ *
+ * `nghttp3_check_header_name` returns nonzero if HTTP header field
+ * name |name| of length |len| is valid according to
+ * :rfc:`7230#section-3.2`.
+ *
+ * Because this is a header field name in HTTP/3, the upper cased
+ * alphabet is treated as error.
+ */
+NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len);
+
+/**
+ * @function
+ *
+ * `nghttp3_check_header_value` returns nonzero if HTTP header field
+ * value |value| of length |len| is valid according to
+ * :rfc:`7230#section-3.2`.
+ */
+NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len);
+
+/**
+ * @function
+ *
+ * `nghttp3_http_parse_priority` parses priority HTTP header field
+ * stored in the buffer pointed by |value| of length |len|. If it
+ * successfully processed header field value, it stores the result
+ * into |*dest|. This function just overwrites what it sees in the
+ * header field value and does not initialize any field in |*dest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * The function could not parse the provided value.
+ */
+NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest,
+ const uint8_t *value,
+ size_t len);
+
+/**
+ * @macrosection
+ *
+ * nghttp3_info flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_VERSION_AGE` is the age of :type:`nghttp3_info`.
+ */
+#define NGHTTP3_VERSION_AGE 1
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_info` is what `nghttp3_version()` returns. It holds
+ * information about the particular nghttp3 version.
+ */
+typedef struct nghttp3_info {
+ /**
+ * :member:`age` is the age of this struct. This instance of
+ * nghttp3 sets it to :macro:`NGHTTP3_VERSION_AGE` but a future
+ * version may bump it and add more struct fields at the bottom
+ */
+ int age;
+ /**
+ * :member:`version_num` is the :macro:`NGHTTP3_VERSION_NUM` number
+ * (since age ==1)
+ */
+ int version_num;
+ /**
+ * :member:`version_str` points to the :macro:`NGHTTP3_VERSION`
+ * string (since age ==1)
+ */
+ const char *version_str;
+ /* -------- the above fields all exist when age == 1 */
+} nghttp3_info;
+
+/**
+ * @function
+ *
+ * `nghttp3_version` returns a pointer to a :type:`nghttp3_info`
+ * struct with version information about the run-time library in use.
+ * The |least_version| argument can be set to a 24 bit numerical value
+ * for the least accepted version number and if the condition is not
+ * met, this function will return a ``NULL``. Pass in 0 to skip the
+ * version checking.
+ */
+NGHTTP3_EXTERN const nghttp3_info *nghttp3_version(int least_version);
+
+/**
+ * @function
+ *
+ * `nghttp3_err_is_fatal` returns nonzero if |liberr| is a fatal
+ * error. |liberr| must be one of nghttp3 library error codes (which
+ * is defined as NGHTTP3_ERR_* macro, such as
+ * :macro:`NGHTTP3_ERR_NOMEM`).
+ */
+NGHTTP3_EXTERN int nghttp3_err_is_fatal(int liberr);
+
+/*
+ * Versioned function wrappers
+ */
+
+/*
+ * `nghttp3_settings_default` is a wrapper around
+ * `nghttp3_settings_default_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_settings_default(SETTINGS) \
+ nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_VERSION, (SETTINGS))
+
+/*
+ * `nghttp3_conn_client_new` is a wrapper around
+ * `nghttp3_conn_client_new_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_conn_client_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \
+ nghttp3_conn_client_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \
+ (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \
+ (SETTINGS), (MEM), (USER_DATA))
+
+/*
+ * `nghttp3_conn_server_new` is a wrapper around
+ * `nghttp3_conn_server_new_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_conn_server_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \
+ nghttp3_conn_server_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \
+ (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \
+ (SETTINGS), (MEM), (USER_DATA))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGHTTP3_H */
diff --git a/lib/includes/nghttp3/version.h.in b/lib/includes/nghttp3/version.h.in
new file mode 100644
index 0000000..1d86147
--- /dev/null
+++ b/lib/includes/nghttp3/version.h.in
@@ -0,0 +1,46 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_VERSION_H
+#define NGHTTP3_VERSION_H
+
+/**
+ * @macro
+ *
+ * Version number of the nghttp3 library release.
+ */
+#define NGHTTP3_VERSION "@PACKAGE_VERSION@"
+
+/**
+ * @macro
+ *
+ * Numerical representation of the version number of the nghttp3
+ * library release. This is a 24 bit number with 8 bits for major
+ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3
+ * becomes 0x010203.
+ */
+#define NGHTTP3_VERSION_NUM @PACKAGE_VERSION_NUM@
+
+#endif /* NGHTTP3_VERSION_H */
diff --git a/lib/libnghttp3.pc.in b/lib/libnghttp3.pc.in
new file mode 100644
index 0000000..c109b3e
--- /dev/null
+++ b/lib/libnghttp3.pc.in
@@ -0,0 +1,34 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2016 ngtcp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnghttp3
+Description: nghttp3 library
+URL: https://github.com/ngtcp2/nghttp3
+Version: @VERSION@
+Libs: -L${libdir} -lnghttp3
+Cflags: -I${includedir}
diff --git a/lib/nghttp3_balloc.c b/lib/nghttp3_balloc.c
new file mode 100644
index 0000000..e134d0f
--- /dev/null
+++ b/lib/nghttp3_balloc.c
@@ -0,0 +1,91 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_balloc.h"
+
+#include <assert.h>
+
+#include "nghttp3_mem.h"
+
+void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
+ const nghttp3_mem *mem) {
+ assert((blklen & 0xfu) == 0);
+
+ balloc->mem = mem;
+ balloc->blklen = blklen;
+ balloc->head = NULL;
+ nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
+}
+
+void nghttp3_balloc_free(nghttp3_balloc *balloc) {
+ if (balloc == NULL) {
+ return;
+ }
+
+ nghttp3_balloc_clear(balloc);
+}
+
+void nghttp3_balloc_clear(nghttp3_balloc *balloc) {
+ nghttp3_memblock_hd *p, *next;
+
+ for (p = balloc->head; p; p = next) {
+ next = p->next;
+ nghttp3_mem_free(balloc->mem, p);
+ }
+
+ balloc->head = NULL;
+ nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
+}
+
+int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n) {
+ uint8_t *p;
+ nghttp3_memblock_hd *hd;
+
+ assert(n <= balloc->blklen);
+
+ if (nghttp3_buf_left(&balloc->buf) < n) {
+ p = nghttp3_mem_malloc(balloc->mem, sizeof(nghttp3_memblock_hd) + 0x10u +
+ balloc->blklen);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ hd = (nghttp3_memblock_hd *)(void *)p;
+ hd->next = balloc->head;
+ balloc->head = hd;
+ nghttp3_buf_wrap_init(
+ &balloc->buf,
+ (uint8_t *)(((uintptr_t)p + sizeof(nghttp3_memblock_hd) + 0xfu) &
+ ~(uintptr_t)0xfu),
+ balloc->blklen);
+ }
+
+ assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
+
+ *pbuf = balloc->buf.last;
+ balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
+
+ return 0;
+}
diff --git a/lib/nghttp3_balloc.h b/lib/nghttp3_balloc.h
new file mode 100644
index 0000000..e02f61d
--- /dev/null
+++ b/lib/nghttp3_balloc.h
@@ -0,0 +1,92 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_BALLOC_H
+#define NGHTTP3_BALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_buf.h"
+
+typedef struct nghttp3_memblock_hd nghttp3_memblock_hd;
+
+/*
+ * nghttp3_memblock_hd is the header of memory block.
+ */
+struct nghttp3_memblock_hd {
+ nghttp3_memblock_hd *next;
+};
+
+/*
+ * nghttp3_balloc is a custom memory allocator. It allocates |blklen|
+ * bytes of memory at once on demand, and returns its slice when the
+ * allocation is requested.
+ */
+typedef struct nghttp3_balloc {
+ /* mem is the underlying memory allocator. */
+ const nghttp3_mem *mem;
+ /* blklen is the size of memory block. */
+ size_t blklen;
+ /* head points to the list of memory block allocated so far. */
+ nghttp3_memblock_hd *head;
+ /* buf wraps the current memory block for allocation requests. */
+ nghttp3_buf buf;
+} nghttp3_balloc;
+
+/*
+ * nghttp3_balloc_init initializes |balloc| with |blklen| which is the
+ * size of memory block.
+ */
+void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_balloc_free releases all allocated memory blocks.
+ */
+void nghttp3_balloc_free(nghttp3_balloc *balloc);
+
+/*
+ * nghttp3_balloc_get allocates |n| bytes of memory and assigns its
+ * pointer to |*pbuf|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n);
+
+/*
+ * nghttp3_balloc_clear releases all allocated memory blocks and
+ * initializes its state.
+ */
+void nghttp3_balloc_clear(nghttp3_balloc *balloc);
+
+#endif /* NGHTTP3_BALLOC_H */
diff --git a/lib/nghttp3_buf.c b/lib/nghttp3_buf.c
new file mode 100644
index 0000000..aae075a
--- /dev/null
+++ b/lib/nghttp3_buf.c
@@ -0,0 +1,90 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_buf.h"
+
+void nghttp3_buf_init(nghttp3_buf *buf) {
+ buf->begin = buf->end = buf->pos = buf->last = NULL;
+}
+
+void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len) {
+ buf->begin = buf->pos = buf->last = src;
+ buf->end = buf->begin + len;
+}
+
+void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, buf->begin);
+}
+
+size_t nghttp3_buf_left(const nghttp3_buf *buf) {
+ return (size_t)(buf->end - buf->last);
+}
+
+size_t nghttp3_buf_len(const nghttp3_buf *buf) {
+ return (size_t)(buf->last - buf->pos);
+}
+
+size_t nghttp3_buf_cap(const nghttp3_buf *buf) {
+ return (size_t)(buf->end - buf->begin);
+}
+
+void nghttp3_buf_reset(nghttp3_buf *buf) { buf->pos = buf->last = buf->begin; }
+
+int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem) {
+ uint8_t *p;
+ nghttp3_ssize pos_offset, last_offset;
+
+ if ((size_t)(buf->end - buf->begin) >= size) {
+ return 0;
+ }
+
+ pos_offset = buf->pos - buf->begin;
+ last_offset = buf->last - buf->begin;
+
+ p = nghttp3_mem_realloc(mem, buf->begin, size);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ buf->begin = p;
+ buf->end = p + size;
+ buf->pos = p + pos_offset;
+ buf->last = p + last_offset;
+
+ return 0;
+}
+
+void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b) {
+ nghttp3_buf c = *a;
+
+ *a = *b;
+ *b = c;
+}
+
+void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
+ nghttp3_buf_type type) {
+ tbuf->buf = *buf;
+ tbuf->type = type;
+}
diff --git a/lib/nghttp3_buf.h b/lib/nghttp3_buf.h
new file mode 100644
index 0000000..472a4b7
--- /dev/null
+++ b/lib/nghttp3_buf.h
@@ -0,0 +1,74 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_BUF_H
+#define NGHTTP3_BUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len);
+
+/*
+ * nghttp3_buf_cap returns the capacity of the buffer. In other
+ * words, it returns buf->end - buf->begin.
+ */
+size_t nghttp3_buf_cap(const nghttp3_buf *buf);
+
+int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_buf_swap swaps |a| and |b|.
+ */
+void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b);
+
+typedef enum nghttp3_buf_type {
+ /* NGHTTP3_BUF_TYPE_PRIVATE indicates that memory is allocated for
+ this buffer only and should be freed after its use. */
+ NGHTTP3_BUF_TYPE_PRIVATE,
+ /* NGHTTP3_BUF_TYPE_SHARED indicates that buffer points to shared
+ memory. */
+ NGHTTP3_BUF_TYPE_SHARED,
+ /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a
+ memory which comes from outside of the library. */
+ NGHTTP3_BUF_TYPE_ALIEN,
+} nghttp3_buf_type;
+
+typedef struct nghttp3_typed_buf {
+ nghttp3_buf buf;
+ nghttp3_buf_type type;
+} nghttp3_typed_buf;
+
+void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
+ nghttp3_buf_type type);
+
+void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf);
+
+#endif /* NGHTTP3_BUF_H */
diff --git a/lib/nghttp3_conn.c b/lib/nghttp3_conn.c
new file mode 100644
index 0000000..c52be96
--- /dev/null
+++ b/lib/nghttp3_conn.c
@@ -0,0 +1,2532 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_conn.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_err.h"
+#include "nghttp3_conv.h"
+#include "nghttp3_http.h"
+#include "nghttp3_unreachable.h"
+
+/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the
+ dynamic table capacity that QPACK encoder is willing to use. */
+#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096
+
+/*
+ * conn_remote_stream_uni returns nonzero if |stream_id| is remote
+ * unidirectional stream ID.
+ */
+static int conn_remote_stream_uni(nghttp3_conn *conn, int64_t stream_id) {
+ if (conn->server) {
+ return (stream_id & 0x03) == 0x02;
+ }
+ return (stream_id & 0x03) == 0x03;
+}
+
+static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.begin_headers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.begin_headers(conn, stream->node.id, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream,
+ int fin) {
+ int rv;
+
+ if (!conn->callbacks.end_headers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.end_headers(conn, stream->node.id, fin, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_begin_trailers(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.begin_trailers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.begin_trailers(conn, stream->node.id, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream,
+ int fin) {
+ int rv;
+
+ if (!conn->callbacks.end_trailers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.end_trailers(conn, stream->node.id, fin, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.end_stream) {
+ return 0;
+ }
+
+ rv = conn->callbacks.end_stream(conn, stream->node.id, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_stop_sending(nghttp3_conn *conn, nghttp3_stream *stream,
+ uint64_t app_error_code) {
+ int rv;
+
+ if (!conn->callbacks.stop_sending) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stop_sending(conn, stream->node.id, app_error_code,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream,
+ uint64_t app_error_code) {
+ int rv;
+
+ if (!conn->callbacks.reset_stream) {
+ return 0;
+ }
+
+ rv = conn->callbacks.reset_stream(conn, stream->node.id, app_error_code,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_deferred_consume(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ size_t nconsumed) {
+ int rv;
+
+ if (nconsumed == 0 || !conn->callbacks.deferred_consume) {
+ return 0;
+ }
+
+ rv = conn->callbacks.deferred_consume(conn, stream->node.id, nconsumed,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int ricnt_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ nghttp3_stream *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_stream, qpack_blocked_pe);
+ nghttp3_stream *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_stream, qpack_blocked_pe);
+
+ return lhs->qpack_sctx.ricnt < rhs->qpack_sctx.ricnt;
+}
+
+static int cycle_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ const nghttp3_tnode *lhs = nghttp3_struct_of(lhsx, nghttp3_tnode, pe);
+ const nghttp3_tnode *rhs = nghttp3_struct_of(rhsx, nghttp3_tnode, pe);
+
+ if (lhs->cycle == rhs->cycle) {
+ return lhs->id < rhs->id;
+ }
+
+ return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP;
+}
+
+static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
+ const nghttp3_callbacks *callbacks, int settings_version,
+ const nghttp3_settings *settings, const nghttp3_mem *mem,
+ void *user_data) {
+ int rv;
+ nghttp3_conn *conn;
+ size_t i;
+ (void)callbacks_version;
+ (void)settings_version;
+
+ if (mem == NULL) {
+ mem = nghttp3_mem_default();
+ }
+
+ conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn));
+ if (conn == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_objalloc_init(&conn->out_chunk_objalloc,
+ NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem);
+ nghttp3_objalloc_stream_init(&conn->stream_objalloc, 64, mem);
+
+ nghttp3_map_init(&conn->streams, mem);
+
+ rv = nghttp3_qpack_decoder_init(&conn->qdec,
+ settings->qpack_max_dtable_capacity,
+ settings->qpack_blocked_streams, mem);
+ if (rv != 0) {
+ goto qdec_init_fail;
+ }
+
+ rv = nghttp3_qpack_encoder_init(
+ &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem);
+ if (rv != 0) {
+ goto qenc_init_fail;
+ }
+
+ nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem);
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem);
+ }
+
+ nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem);
+
+ conn->callbacks = *callbacks;
+ conn->local.settings = *settings;
+ if (!server) {
+ conn->local.settings.enable_connect_protocol = 0;
+ }
+ nghttp3_settings_default(&conn->remote.settings);
+ conn->mem = mem;
+ conn->user_data = user_data;
+ conn->server = server;
+ conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1;
+ conn->tx.goaway_id = NGHTTP3_VARINT_MAX + 1;
+ conn->rx.max_stream_id_bidi = -4;
+
+ *pconn = conn;
+
+ return 0;
+
+qenc_init_fail:
+ nghttp3_qpack_decoder_free(&conn->qdec);
+qdec_init_fail:
+ nghttp3_map_free(&conn->streams);
+ nghttp3_objalloc_free(&conn->stream_objalloc);
+ nghttp3_objalloc_free(&conn->out_chunk_objalloc);
+ nghttp3_mem_free(mem, conn);
+
+ return rv;
+}
+
+int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn,
+ int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *user_data) {
+ int rv;
+
+ rv = conn_new(pconn, /* server = */ 0, callbacks_version, callbacks,
+ settings_version, settings, mem, user_data);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_server_new_versioned(nghttp3_conn **pconn,
+ int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *user_data) {
+ int rv;
+
+ rv = conn_new(pconn, /* server = */ 1, callbacks_version, callbacks,
+ settings_version, settings, mem, user_data);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+static int free_stream(void *data, void *ptr) {
+ nghttp3_stream *stream = data;
+
+ (void)ptr;
+
+ nghttp3_stream_del(stream);
+
+ return 0;
+}
+
+void nghttp3_conn_del(nghttp3_conn *conn) {
+ size_t i;
+
+ if (conn == NULL) {
+ return;
+ }
+
+ nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem);
+ nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem);
+
+ nghttp3_idtr_free(&conn->remote.bidi.idtr);
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ nghttp3_pq_free(&conn->sched[i].spq);
+ }
+
+ nghttp3_pq_free(&conn->qpack_blocked_streams);
+
+ nghttp3_qpack_encoder_free(&conn->qenc);
+ nghttp3_qpack_decoder_free(&conn->qdec);
+
+ nghttp3_map_each_free(&conn->streams, free_stream, NULL);
+ nghttp3_map_free(&conn->streams);
+
+ nghttp3_objalloc_free(&conn->stream_objalloc);
+ nghttp3_objalloc_free(&conn->out_chunk_objalloc);
+
+ nghttp3_mem_free(conn->mem, conn);
+}
+
+static int conn_bidi_idtr_open(nghttp3_conn *conn, int64_t stream_id) {
+ int rv;
+
+ rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (nghttp3_ksl_len(&conn->remote.bidi.idtr.gap.gap) > 32) {
+ nghttp3_gaptr_drop_first_gap(&conn->remote.bidi.idtr.gap);
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_stream *stream;
+ size_t bidi_nproc;
+ int rv;
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ /* TODO Assert idtr */
+ /* QUIC transport ensures that this is new stream. */
+ if (conn->server) {
+ if (nghttp3_client_stream_bidi(stream_id)) {
+ rv = conn_bidi_idtr_open(conn, stream_id);
+ if (rv != 0) {
+ if (nghttp3_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ /* Ignore return value. We might drop the first gap if there
+ are many gaps if QUIC stack allows too many holes in stream
+ ID space. idtr is used to decide whether PRIORITY_UPDATE
+ frame should be ignored or not and the frame is optional.
+ Ignoring them causes no harm. */
+ }
+
+ conn->rx.max_stream_id_bidi =
+ nghttp3_max(conn->rx.max_stream_id_bidi, stream_id);
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
+ conn->tx.goaway_id <= stream_id) {
+ stream->rstate.state = NGHTTP3_REQ_STREAM_STATE_IGN_REST;
+
+ rv = nghttp3_conn_reject_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else {
+ /* unidirectional stream */
+ if (srclen == 0 && fin) {
+ return 0;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ } else if (nghttp3_stream_uni(stream_id)) {
+ if (srclen == 0 && fin) {
+ return 0;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ } else {
+ /* client doesn't expect to receive new bidirectional stream
+ from server. */
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ } else if (conn->server) {
+ if (nghttp3_client_stream_bidi(stream_id)) {
+ if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) {
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ }
+ }
+ }
+
+ if (srclen == 0 && !fin) {
+ return 0;
+ }
+
+ if (nghttp3_stream_uni(stream_id)) {
+ return nghttp3_conn_read_uni(conn, stream, src, srclen, fin);
+ }
+
+ if (fin) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF;
+ }
+ return nghttp3_conn_read_bidi(conn, &bidi_nproc, stream, src, srclen, fin);
+}
+
+static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_stream_read_state *rstate = &stream->rstate;
+ nghttp3_varint_read_state *rvint = &rstate->rvint;
+ nghttp3_ssize nread;
+ int64_t stream_type;
+
+ assert(srclen);
+
+ nread = nghttp3_read_varint(rvint, src, srclen, fin);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ if (rvint->left) {
+ return nread;
+ }
+
+ stream_type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ switch (stream_type) {
+ case NGHTTP3_STREAM_TYPE_CONTROL:
+ if (conn->flags & NGHTTP3_CONN_FLAG_CONTROL_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_CONTROL_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_CONTROL;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE;
+ break;
+ case NGHTTP3_STREAM_TYPE_PUSH:
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ case NGHTTP3_STREAM_TYPE_QPACK_ENCODER:
+ if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_DECODER:
+ if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER;
+ break;
+ default:
+ stream->type = NGHTTP3_STREAM_TYPE_UNKNOWN;
+ break;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED;
+
+ return nread;
+}
+
+static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_ssize nread = 0;
+ nghttp3_ssize nconsumed = 0;
+ int rv;
+
+ assert(srclen || fin);
+
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) {
+ if (srclen == 0 && fin) {
+ /* Ignore stream if it is closed before reading stream header.
+ If it is closed while reading it, return error, making it
+ consistent in our code base. */
+ if (stream->rstate.rvint.left) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ rv = conn_delete_stream(conn, stream);
+ assert(0 == rv);
+
+ return 0;
+ }
+ nread = conn_read_type(conn, stream, src, srclen, fin);
+ if (nread < 0) {
+ return (int)nread;
+ }
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) {
+ assert((size_t)nread == srclen);
+ return (nghttp3_ssize)srclen;
+ }
+
+ src += nread;
+ srclen -= (size_t)nread;
+
+ if (srclen == 0) {
+ return nread;
+ }
+ }
+
+ switch (stream->type) {
+ case NGHTTP3_STREAM_TYPE_CONTROL:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+ nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_ENCODER:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+ nconsumed = nghttp3_conn_read_qpack_encoder(conn, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_DECODER:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+ nconsumed = nghttp3_conn_read_qpack_decoder(conn, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_UNKNOWN:
+ nconsumed = (nghttp3_ssize)srclen;
+
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (nconsumed < 0) {
+ return nconsumed;
+ }
+
+ return nread + nconsumed;
+}
+
+static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) {
+ return (int64_t)len >= rstate->left;
+}
+
+nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen) {
+ const uint8_t *p = src, *end = src + srclen;
+ int rv;
+ nghttp3_stream_read_state *rstate = &stream->rstate;
+ nghttp3_varint_read_state *rvint = &rstate->rvint;
+ nghttp3_ssize nread;
+ size_t nconsumed = 0;
+ int busy = 0;
+ size_t len;
+ const uint8_t *pri_field_value = NULL;
+ size_t pri_field_valuelen = 0;
+
+ assert(srclen);
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (rstate->state) {
+ case NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->fr.hd.type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH;
+ if (p == end) {
+ break;
+ }
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->left = rstate->fr.hd.length = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) {
+ if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) {
+ return NGHTTP3_ERR_H3_MISSING_SETTINGS;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_SETTINGS_RECVED;
+ } else if (rstate->fr.hd.type == NGHTTP3_FRAME_SETTINGS) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+
+ switch (rstate->fr.hd.type) {
+ case NGHTTP3_FRAME_SETTINGS:
+ /* SETTINGS frame might be empty. */
+ if (rstate->left == 0) {
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS;
+ break;
+ case NGHTTP3_FRAME_GOAWAY:
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_GOAWAY;
+ break;
+ case NGHTTP3_FRAME_MAX_PUSH_ID:
+ if (!conn->server) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID;
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ if (!conn->server) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID;
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
+ /* We do not support push */
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */
+ case NGHTTP3_FRAME_DATA:
+ case NGHTTP3_FRAME_HEADERS:
+ case NGHTTP3_FRAME_PUSH_PROMISE:
+ case NGHTTP3_H2_FRAME_PRIORITY:
+ case NGHTTP3_H2_FRAME_PING:
+ case NGHTTP3_H2_FRAME_WINDOW_UPDATE:
+ case NGHTTP3_H2_FRAME_CONTINUATION:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ default:
+ /* TODO Handle reserved frame type */
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
+ break;
+ }
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS:
+ for (; p != end;) {
+ if (rstate->left == 0) {
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ /* Read Identifier */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID;
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ /* Read Value */
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ len -= (size_t)nread;
+ if (len == 0) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+ break;
+ }
+
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ rv =
+ nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+
+ if (p == end) {
+ return (nghttp3_ssize)nconsumed;
+ }
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ rv = nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (rstate->left) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS;
+ break;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_GOAWAY:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ if (!conn->server && !nghttp3_client_stream_bidi(rvint->acc)) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+ if (conn->rx.goaway_id < rvint->acc) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_RECVED;
+ conn->rx.goaway_id = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (conn->callbacks.shutdown) {
+ rv =
+ conn->callbacks.shutdown(conn, conn->rx.goaway_id, conn->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID:
+ /* server side only */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ if (conn->local.uni.max_pushes > (uint64_t)rvint->acc + 1) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ conn->local.uni.max_pushes = (uint64_t)rvint->acc + 1;
+ nghttp3_varint_read_state_reset(rvint);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID:
+ /* server side only */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->fr.priority_update.pri_elem_id = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (rstate->left == 0) {
+ rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY;
+ rstate->fr.priority_update.pri.inc = 0;
+
+ rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE;
+
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE:
+ /* We need to buffer Priority Field Value because it might be
+ fragmented. */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ if (conn->rx.pri_fieldbuflen == 0 && rstate->left == (int64_t)len) {
+ /* Everything is in the input buffer. Apply same length
+ limit we impose when buffering the field. */
+ if (len > sizeof(conn->rx.pri_fieldbuf)) {
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
+ break;
+ }
+
+ pri_field_value = p;
+ pri_field_valuelen = len;
+ } else if (len + conn->rx.pri_fieldbuflen >
+ sizeof(conn->rx.pri_fieldbuf)) {
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
+ break;
+ } else {
+ memcpy(conn->rx.pri_fieldbuf + conn->rx.pri_fieldbuflen, p, len);
+ conn->rx.pri_fieldbuflen += len;
+
+ if (rstate->left == (int64_t)len) {
+ pri_field_value = conn->rx.pri_fieldbuf;
+ pri_field_valuelen = conn->rx.pri_fieldbuflen;
+ }
+ }
+
+ p += len;
+ nconsumed += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY;
+ rstate->fr.priority_update.pri.inc = 0;
+
+ if (nghttp3_http_parse_priority(&rstate->fr.priority_update.pri,
+ pri_field_value,
+ pri_field_valuelen) != 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->rx.pri_fieldbuflen = 0;
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ p += len;
+ nconsumed += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ }
+
+ return (nghttp3_ssize)nconsumed;
+}
+
+static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int bidi = nghttp3_client_stream_bidi(stream->node.id);
+ int rv;
+
+ rv = conn_call_deferred_consume(conn, stream,
+ nghttp3_stream_get_buffered_datalen(stream));
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (bidi && conn->callbacks.stream_close) {
+ rv = conn->callbacks.stream_close(conn, stream->node.id, stream->error_code,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ rv =
+ nghttp3_map_remove(&conn->streams, (nghttp3_map_key_type)stream->node.id);
+
+ assert(0 == rv);
+
+ nghttp3_stream_del(stream);
+
+ return 0;
+}
+
+static int conn_process_blocked_stream_data(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ nghttp3_buf *buf;
+ size_t nproc;
+ nghttp3_ssize nconsumed;
+ int rv;
+ size_t len;
+
+ assert(nghttp3_client_stream_bidi(stream->node.id));
+
+ for (;;) {
+ len = nghttp3_ringbuf_len(&stream->inq);
+ if (len == 0) {
+ break;
+ }
+
+ buf = nghttp3_ringbuf_get(&stream->inq, 0);
+
+ nconsumed = nghttp3_conn_read_bidi(
+ conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf),
+ len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF));
+ if (nconsumed < 0) {
+ return (int)nconsumed;
+ }
+
+ buf->pos += nproc;
+
+ rv = conn_call_deferred_consume(conn, stream, (size_t)nconsumed);
+ if (rv != 0) {
+ return 0;
+ }
+
+ if (nghttp3_buf_len(buf) == 0) {
+ nghttp3_buf_free(buf, stream->mem);
+ nghttp3_ringbuf_pop_front(&stream->inq);
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
+ break;
+ }
+ }
+
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) &&
+ (stream->flags & NGHTTP3_STREAM_FLAG_CLOSED)) {
+ assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX);
+
+ rv = conn_delete_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen) {
+ nghttp3_ssize nconsumed =
+ nghttp3_qpack_decoder_read_encoder(&conn->qdec, src, srclen);
+ nghttp3_stream *stream;
+ int rv;
+
+ if (nconsumed < 0) {
+ return nconsumed;
+ }
+
+ for (; !nghttp3_pq_empty(&conn->qpack_blocked_streams);) {
+ stream = nghttp3_struct_of(nghttp3_pq_top(&conn->qpack_blocked_streams),
+ nghttp3_stream, qpack_blocked_pe);
+ if (nghttp3_qpack_stream_context_get_ricnt(&stream->qpack_sctx) >
+ nghttp3_qpack_decoder_get_icnt(&conn->qdec)) {
+ break;
+ }
+
+ nghttp3_conn_qpack_blocked_streams_pop(conn);
+ stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED;
+
+ rv = conn_process_blocked_stream_data(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nconsumed;
+}
+
+nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen) {
+ return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen);
+}
+
+static nghttp3_tnode *stream_get_sched_node(nghttp3_stream *stream) {
+ return &stream->node;
+}
+
+static int conn_update_stream_priority(nghttp3_conn *conn,
+ nghttp3_stream *stream, uint8_t pri) {
+ assert(nghttp3_client_stream_bidi(stream->node.id));
+
+ if (stream->node.pri == pri) {
+ return 0;
+ }
+
+ nghttp3_conn_unschedule_stream(conn, stream);
+
+ stream->node.pri = pri;
+
+ if (nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_schedule_stream(conn, stream);
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
+ nghttp3_stream *stream, const uint8_t *src,
+ size_t srclen, int fin) {
+ const uint8_t *p = src, *end = src ? src + srclen : src;
+ int rv;
+ nghttp3_stream_read_state *rstate = &stream->rstate;
+ nghttp3_varint_read_state *rvint = &rstate->rvint;
+ nghttp3_ssize nread;
+ size_t nconsumed = 0;
+ int busy = 0;
+ size_t len;
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) {
+ *pnproc = srclen;
+
+ return (nghttp3_ssize)srclen;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
+ *pnproc = 0;
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
+ if (rv != 0) {
+ return rv;
+ }
+ return 0;
+ }
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (rstate->state) {
+ case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ goto almost_done;
+ }
+
+ rstate->fr.hd.type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH;
+ if (p == end) {
+ goto almost_done;
+ }
+ /* Fall through */
+ case NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ goto almost_done;
+ }
+
+ rstate->left = rstate->fr.hd.length = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ switch (rstate->fr.hd.type) {
+ case NGHTTP3_FRAME_DATA:
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN);
+ if (rv != 0) {
+ return rv;
+ }
+ /* DATA frame might be empty. */
+ if (rstate->left == 0) {
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_DATA_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_DATA;
+ break;
+ case NGHTTP3_FRAME_HEADERS:
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN);
+ if (rv != 0) {
+ return rv;
+ }
+ if (rstate->left == 0) {
+ rv = nghttp3_stream_empty_headers_allowed(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_HEADERS_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = conn_call_begin_headers(conn, stream);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = conn_call_begin_trailers(conn, stream);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (rv != 0) {
+ return rv;
+ }
+
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS;
+ break;
+ case NGHTTP3_FRAME_PUSH_PROMISE: /* We do not support push */
+ case NGHTTP3_FRAME_CANCEL_PUSH:
+ case NGHTTP3_FRAME_SETTINGS:
+ case NGHTTP3_FRAME_GOAWAY:
+ case NGHTTP3_FRAME_MAX_PUSH_ID:
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
+ case NGHTTP3_H2_FRAME_PRIORITY:
+ case NGHTTP3_H2_FRAME_PING:
+ case NGHTTP3_H2_FRAME_WINDOW_UPDATE:
+ case NGHTTP3_H2_FRAME_CONTINUATION:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ default:
+ /* TODO Handle reserved frame type */
+ busy = 1;
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_FRAME;
+ break;
+ }
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_DATA:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ rv = nghttp3_conn_on_data(conn, stream, p, len);
+ if (rv != 0) {
+ return rv;
+ }
+ p += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ goto almost_done;
+ }
+
+ rv = nghttp3_stream_transit_rx_http_state(stream,
+ NGHTTP3_HTTP_EVENT_DATA_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_HEADERS:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ nread = nghttp3_conn_on_headers(conn, stream, p, len,
+ (int64_t)len == rstate->left);
+ if (nread < 0) {
+ if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
+ goto http_header_error;
+ }
+
+ return nread;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
+ if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) {
+ rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ *pnproc = (size_t)(p - src);
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ if (rstate->left) {
+ goto almost_done;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ rv = nghttp3_http_on_request_headers(&stream->rx.http);
+ break;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = nghttp3_http_on_response_headers(&stream->rx.http);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = 0;
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (rv != 0) {
+ if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
+ goto http_header_error;
+ }
+
+ return rv;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ /* Only server utilizes priority information to schedule
+ streams. */
+ if (conn->server &&
+ (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_PRIORITY) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET)) {
+ rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ /* fall through */
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = conn_call_end_headers(conn, stream, p == end && fin);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = conn_call_end_trailers(conn, stream, p == end && fin);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_stream_transit_rx_http_state(stream,
+ NGHTTP3_HTTP_EVENT_HEADERS_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+
+ break;
+
+ http_header_error:
+ stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR;
+
+ busy = 1;
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST;
+
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ p += len;
+ nconsumed += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ goto almost_done;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_IGN_REST:
+ nconsumed += (size_t)(end - p);
+ *pnproc = (size_t)(end - src);
+ return (nghttp3_ssize)nconsumed;
+ }
+ }
+
+almost_done:
+ if (fin) {
+ switch (rstate->state) {
+ case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE:
+ if (rvint->left) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+ rv = nghttp3_stream_transit_rx_http_state(stream,
+ NGHTTP3_HTTP_EVENT_MSG_END);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = conn_call_end_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_IGN_REST:
+ break;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ }
+
+ *pnproc = (size_t)(p - src);
+ return (nghttp3_ssize)nconsumed;
+}
+
+int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *data, size_t datalen) {
+ int rv;
+
+ rv = nghttp3_http_on_data_chunk(stream, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!conn->callbacks.recv_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_data(conn, stream->node.id, data, datalen,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) {
+ uint32_t urgency = nghttp3_pri_uint8_urgency(tnode->pri);
+
+ assert(urgency < NGHTTP3_URGENCY_LEVELS);
+
+ return &conn->sched[urgency].spq;
+}
+
+static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_ssize nread;
+ int rv;
+ nghttp3_qpack_decoder *qdec = &conn->qdec;
+ nghttp3_qpack_nv nv;
+ uint8_t flags;
+ nghttp3_buf buf;
+ nghttp3_recv_header recv_header = NULL;
+ nghttp3_http_state *http;
+ int request = 0;
+ int trailers = 0;
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ request = 1;
+ /* Fall through */
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ recv_header = conn->callbacks.recv_header;
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ request = 1;
+ /* Fall through */
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ trailers = 1;
+ recv_header = conn->callbacks.recv_trailer;
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ http = &stream->rx.http;
+
+ nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen);
+ buf.last = buf.end;
+
+ for (;;) {
+ nread = nghttp3_qpack_decoder_read_request(qdec, &stream->qpack_sctx, &nv,
+ &flags, buf.pos,
+ nghttp3_buf_len(&buf), fin);
+
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ buf.pos += nread;
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) {
+ if (conn->local.settings.qpack_blocked_streams <=
+ nghttp3_pq_size(&conn->qpack_blocked_streams)) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED;
+ rv = nghttp3_conn_qpack_blocked_streams_push(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ }
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ nghttp3_qpack_stream_context_reset(&stream->qpack_sctx);
+ break;
+ }
+
+ if (nread == 0) {
+ break;
+ }
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
+ rv = nghttp3_http_on_header(
+ http, &nv, request, trailers,
+ conn->server && conn->local.settings.enable_connect_protocol);
+ switch (rv) {
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ break;
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ rv = 0;
+ break;
+ case 0:
+ if (recv_header) {
+ rv = recv_header(conn, stream->node.id, nv.token, nv.name, nv.value,
+ nv.flags, conn->user_data, stream->user_data);
+ if (rv != 0) {
+ rv = NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ nghttp3_rcbuf_decref(nv.name);
+ nghttp3_rcbuf_decref(nv.value);
+
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ return buf.pos - src;
+}
+
+nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ if (srclen == 0 && !fin) {
+ return 0;
+ }
+
+ return conn_decode_headers(conn, stream, src, srclen, fin);
+}
+
+int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
+ const nghttp3_frame_settings *fr) {
+ const nghttp3_settings_entry *ent = &fr->iv[0];
+ nghttp3_settings *dest = &conn->remote.settings;
+
+ /* TODO Check for duplicates */
+ switch (ent->id) {
+ case NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE:
+ dest->max_field_section_size = ent->value;
+ break;
+ case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY:
+ if (dest->qpack_max_dtable_capacity != 0) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ if (ent->value == 0) {
+ break;
+ }
+
+ dest->qpack_max_dtable_capacity = (size_t)ent->value;
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&conn->qenc,
+ (size_t)ent->value);
+ break;
+ case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS:
+ if (dest->qpack_blocked_streams != 0) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ if (ent->value == 0) {
+ break;
+ }
+
+ dest->qpack_blocked_streams = (size_t)ent->value;
+
+ nghttp3_qpack_encoder_set_max_blocked_streams(
+ &conn->qenc, (size_t)nghttp3_min(100, ent->value));
+ break;
+ case NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL:
+ if (!conn->server) {
+ break;
+ }
+
+ switch (ent->value) {
+ case 0:
+ if (dest->enable_connect_protocol) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ break;
+ case 1:
+ break;
+ default:
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ dest->enable_connect_protocol = (int)ent->value;
+ break;
+ case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH:
+ case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS:
+ case NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE:
+ case NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE:
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ default:
+ /* Ignore unknown settings ID */
+ break;
+ }
+
+ return 0;
+}
+
+static int
+conn_on_priority_update_stream(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr) {
+ int64_t stream_id = fr->pri_elem_id;
+ nghttp3_stream *stream;
+ int rv;
+
+ if (!nghttp3_client_stream_bidi(stream_id) ||
+ nghttp3_ord_stream_id(stream_id) > conn->remote.bidi.max_client_streams) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
+ conn->tx.goaway_id <= stream_id) {
+ /* Connection is going down. Ignore priority signal. */
+ return 0;
+ }
+
+ rv = conn_bidi_idtr_open(conn, stream_id);
+ if (rv != 0) {
+ if (nghttp3_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ assert(rv == NGHTTP3_ERR_STREAM_IN_USE);
+
+ /* The stream is gone. Just ignore. */
+ return 0;
+ }
+
+ conn->rx.max_stream_id_bidi =
+ nghttp3_max(conn->rx.max_stream_id_bidi, stream_id);
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->node.pri = nghttp3_pri_to_uint8(&fr->pri);
+ stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED;
+
+ return 0;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET) {
+ return 0;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED;
+
+ return conn_update_stream_priority(conn, stream,
+ nghttp3_pri_to_uint8(&fr->pri));
+}
+
+int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr) {
+ assert(conn->server);
+ assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE);
+
+ return conn_on_priority_update_stream(conn, fr);
+}
+
+static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id,
+ uint64_t datalen, void *user_data) {
+ nghttp3_conn *conn = stream->conn;
+ int rv;
+
+ if (!conn->callbacks.acked_stream_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.acked_stream_data(conn, stream_id, datalen,
+ conn->user_data, user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+ int rv;
+ nghttp3_stream_callbacks callbacks = {
+ conn_stream_acked_data,
+ };
+
+ rv = nghttp3_stream_new(&stream, stream_id, &callbacks,
+ &conn->out_chunk_objalloc, &conn->stream_objalloc,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->conn = conn;
+
+ rv = nghttp3_map_insert(&conn->streams, (nghttp3_map_key_type)stream->node.id,
+ stream);
+ if (rv != 0) {
+ nghttp3_stream_del(stream);
+ return rv;
+ }
+
+ *pstream = stream;
+
+ return 0;
+}
+
+nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn,
+ int64_t stream_id) {
+ return nghttp3_map_find(&conn->streams, (nghttp3_map_key_type)stream_id);
+}
+
+int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream;
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(!conn->server || nghttp3_server_stream_uni(stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(stream_id));
+
+ if (conn->tx.ctrl) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_CONTROL;
+
+ conn->tx.ctrl = stream;
+
+ rv = nghttp3_stream_write_stream_type(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ frent.aux.settings.local_settings = &conn->local.settings;
+
+ return nghttp3_stream_frq_add(stream, &frent);
+}
+
+int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id,
+ int64_t qdec_stream_id) {
+ nghttp3_stream *stream;
+ int rv;
+
+ assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id));
+ assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(qdec_stream_id));
+
+ if (conn->tx.qenc || conn->tx.qdec) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, qenc_stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+
+ conn->tx.qenc = stream;
+
+ rv = nghttp3_stream_write_stream_type(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, qdec_stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER;
+
+ conn->tx.qdec = stream;
+
+ return nghttp3_stream_write_stream_type(stream);
+}
+
+static nghttp3_ssize conn_writev_stream(nghttp3_conn *conn, int64_t *pstream_id,
+ int *pfin, nghttp3_vec *vec,
+ size_t veccnt, nghttp3_stream *stream) {
+ int rv;
+ nghttp3_ssize n;
+
+ assert(veccnt > 0);
+
+ /* If stream is blocked by read callback, don't attempt to fill
+ more. */
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)) {
+ rv = nghttp3_stream_fill_outq(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (!nghttp3_stream_uni(stream->node.id) && conn->tx.qenc &&
+ !nghttp3_stream_is_blocked(conn->tx.qenc)) {
+ n = nghttp3_stream_writev(conn->tx.qenc, pfin, vec, veccnt);
+ if (n < 0) {
+ return n;
+ }
+ if (n) {
+ *pstream_id = conn->tx.qenc->node.id;
+ return n;
+ }
+ }
+
+ n = nghttp3_stream_writev(stream, pfin, vec, veccnt);
+ if (n < 0) {
+ return n;
+ }
+ /* We might just want to write stream fin without sending any stream
+ data. */
+ if (n == 0 && *pfin == 0) {
+ return 0;
+ }
+
+ *pstream_id = stream->node.id;
+
+ return n;
+}
+
+nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
+ int64_t *pstream_id, int *pfin,
+ nghttp3_vec *vec, size_t veccnt) {
+ nghttp3_ssize ncnt;
+ nghttp3_stream *stream;
+ int rv;
+
+ *pstream_id = -1;
+ *pfin = 0;
+
+ if (veccnt == 0) {
+ return 0;
+ }
+
+ if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) {
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ if (conn->tx.qdec && !nghttp3_stream_is_blocked(conn->tx.qdec)) {
+ rv = nghttp3_stream_write_qpack_decoder_stream(conn->tx.qdec);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qdec);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ if (conn->tx.qenc && !nghttp3_stream_is_blocked(conn->tx.qenc)) {
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qenc);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ stream = nghttp3_conn_get_next_tx_stream(conn);
+ if (stream == NULL) {
+ return 0;
+ }
+
+ ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, stream);
+ if (ncnt < 0) {
+ return ncnt;
+ }
+
+ if (nghttp3_client_stream_bidi(stream->node.id) &&
+ !nghttp3_stream_require_schedule(stream)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+
+ return ncnt;
+}
+
+nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) {
+ size_t i;
+ nghttp3_tnode *tnode;
+ nghttp3_pq *pq;
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ pq = &conn->sched[i].spq;
+ if (nghttp3_pq_empty(pq)) {
+ continue;
+ }
+
+ tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe);
+
+ return nghttp3_struct_of(tnode, nghttp3_stream, node);
+ }
+
+ return NULL;
+}
+
+int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id,
+ size_t n) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+ int rv;
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ rv = nghttp3_stream_add_outq_offset(stream, n);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->unscheduled_nwrite += n;
+
+ if (!nghttp3_client_stream_bidi(stream->node.id)) {
+ return 0;
+ }
+
+ if (!nghttp3_stream_require_schedule(stream)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ return 0;
+ }
+
+ if (stream->unscheduled_nwrite < NGHTTP3_STREAM_MIN_WRITELEN) {
+ return 0;
+ }
+
+ return nghttp3_conn_schedule_stream(conn, stream);
+}
+
+int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t n) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ return nghttp3_stream_add_ack_offset(stream, n);
+}
+
+static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr) {
+ int rv;
+ nghttp3_nv *nnva;
+ nghttp3_frame_entry frent;
+
+ rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ frent.fr.headers.nva = nnva;
+ frent.fr.headers.nvlen = nvlen;
+
+ rv = nghttp3_stream_frq_add(stream, &frent);
+ if (rv != 0) {
+ nghttp3_nva_del(nnva, conn->mem);
+ return rv;
+ }
+
+ if (dr) {
+ frent.fr.hd.type = NGHTTP3_FRAME_DATA;
+ frent.aux.data.dr = *dr;
+
+ rv = nghttp3_stream_frq_add(stream, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_schedule_stream(conn, stream);
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ /* Assume that stream stays on the same urgency level */
+ nghttp3_tnode *node = stream_get_sched_node(stream);
+ int rv;
+
+ rv = nghttp3_tnode_schedule(node, conn_get_sched_pq(conn, node),
+ stream->unscheduled_nwrite);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->unscheduled_nwrite = 0;
+
+ return 0;
+}
+
+int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ if (nghttp3_tnode_is_scheduled(stream_get_sched_node(stream))) {
+ return 0;
+ }
+
+ return nghttp3_conn_schedule_stream(conn, stream);
+}
+
+void nghttp3_conn_unschedule_stream(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ nghttp3_tnode *node = stream_get_sched_node(stream);
+
+ nghttp3_tnode_unschedule(node, conn_get_sched_pq(conn, node));
+}
+
+int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr,
+ void *stream_user_data) {
+ nghttp3_stream *stream;
+ int rv;
+
+ assert(!conn->server);
+ assert(conn->tx.qenc);
+
+ assert(nghttp3_client_stream_bidi(stream_id));
+
+ /* TODO Should we check that stream_id is client stream_id? */
+ /* TODO Check GOAWAY last stream ID */
+ if (nghttp3_stream_uni(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) {
+ return NGHTTP3_ERR_CONN_CLOSING;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream != NULL) {
+ return NGHTTP3_ERR_STREAM_IN_USE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ stream->user_data = stream_user_data;
+
+ nghttp3_http_record_request_method(stream, nva, nvlen);
+
+ if (dr == NULL) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, dr);
+}
+
+int nghttp3_conn_submit_info(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send info (non-final response)
+ now. */
+ assert(conn->server);
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, NULL);
+}
+
+int nghttp3_conn_submit_response(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send response now. */
+ assert(conn->server);
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (dr == NULL) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, dr);
+}
+
+int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send trailer now. */
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, NULL);
+}
+
+int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) {
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(conn->tx.ctrl);
+
+ frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID
+ : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID;
+
+ assert(frent.fr.goaway.id <= conn->tx.goaway_id);
+
+ rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->tx.goaway_id = frent.fr.goaway.id;
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED;
+
+ return 0;
+}
+
+int nghttp3_conn_shutdown(nghttp3_conn *conn) {
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(conn->tx.ctrl);
+
+ frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ if (conn->server) {
+ frent.fr.goaway.id =
+ nghttp3_min((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4);
+ } else {
+ frent.fr.goaway.id = 0;
+ }
+
+ assert(frent.fr.goaway.id <= conn->tx.goaway_id);
+
+ rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->tx.goaway_id = frent.fr.goaway.id;
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED;
+
+ return 0;
+}
+
+int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_reset_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
+}
+
+void nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED;
+ stream->unscheduled_nwrite = 0;
+
+ if (nghttp3_client_stream_bidi(stream->node.id)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+}
+
+void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR;
+ stream->unscheduled_nwrite = 0;
+
+ if (nghttp3_client_stream_bidi(stream->node.id)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+}
+
+int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED;
+
+ if (nghttp3_client_stream_bidi(stream->node.id) &&
+ nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_ensure_stream_scheduled(conn, stream);
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ return (stream->flags &
+ (NGHTTP3_STREAM_FLAG_FC_BLOCKED |
+ NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED | NGHTTP3_STREAM_FLAG_SHUT_WR |
+ NGHTTP3_STREAM_FLAG_CLOSED)) == 0;
+}
+
+int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED;
+
+ if (nghttp3_client_stream_bidi(stream->node.id) &&
+ nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_ensure_stream_scheduled(conn, stream);
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (nghttp3_stream_uni(stream_id) &&
+ stream->type != NGHTTP3_STREAM_TYPE_UNKNOWN) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+
+ stream->error_code = app_error_code;
+
+ nghttp3_conn_unschedule_stream(conn, stream);
+
+ if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ return conn_delete_stream(conn, stream);
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_CLOSED;
+ return 0;
+}
+
+int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return 0;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream) {
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) {
+ return 0;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_RD;
+ }
+
+ return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id);
+}
+
+int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX);
+
+ return nghttp3_pq_push(&conn->qpack_blocked_streams,
+ &stream->qpack_blocked_pe);
+}
+
+void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn) {
+ assert(!nghttp3_pq_empty(&conn->qpack_blocked_streams));
+ nghttp3_pq_pop(&conn->qpack_blocked_streams);
+}
+
+void nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn,
+ uint64_t max_streams) {
+ assert(conn->server);
+ assert(conn->remote.bidi.max_client_streams <= max_streams);
+
+ conn->remote.bidi.max_client_streams = max_streams;
+}
+
+void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn,
+ size_t max_concurrent_streams) {
+ nghttp3_qpack_decoder_set_max_concurrent_streams(&conn->qdec,
+ max_concurrent_streams);
+}
+
+int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id,
+ void *stream_user_data) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ stream->user_data = stream_user_data;
+
+ return 0;
+}
+
+uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
+ int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ return (uint64_t)stream->rstate.left;
+}
+
+int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, nghttp3_pri *dest,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ assert(conn->server);
+
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ dest->urgency = nghttp3_pri_uint8_urgency(stream->node.pri);
+ dest->inc = nghttp3_pri_uint8_inc(stream->node.pri);
+
+ return 0;
+}
+
+int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_pri *pri) {
+ nghttp3_stream *stream;
+ nghttp3_frame_entry frent;
+
+ assert(pri->urgency < NGHTTP3_URGENCY_LEVELS);
+ assert(pri->inc == 0 || pri->inc == 1);
+
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (conn->server) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET;
+
+ return conn_update_stream_priority(conn, stream, nghttp3_pri_to_uint8(pri));
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
+ frent.fr.priority_update.pri_elem_id = stream_id;
+ frent.fr.priority_update.pri = *pri;
+
+ return nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+}
+
+int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ if (!conn_remote_stream_uni(conn, stream_id)) {
+ return 0;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+}
+
+void nghttp3_settings_default_versioned(int settings_version,
+ nghttp3_settings *settings) {
+ (void)settings_version;
+
+ memset(settings, 0, sizeof(nghttp3_settings));
+ settings->max_field_section_size = NGHTTP3_VARINT_MAX;
+ settings->qpack_encoder_max_dtable_capacity =
+ NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY;
+}
diff --git a/lib/nghttp3_conn.h b/lib/nghttp3_conn.h
new file mode 100644
index 0000000..a3f904c
--- /dev/null
+++ b/lib/nghttp3_conn.h
@@ -0,0 +1,200 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_CONN_H
+#define NGHTTP3_CONN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_stream.h"
+#include "nghttp3_map.h"
+#include "nghttp3_qpack.h"
+#include "nghttp3_tnode.h"
+#include "nghttp3_idtr.h"
+#include "nghttp3_gaptr.h"
+
+#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1)
+
+/* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic
+ table size for QPACK encoder. */
+#define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384
+
+/* NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS is the maximum number of
+ blocked streams for QPACK encoder. */
+#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100
+
+/* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_CONN_FLAG_NONE 0x0000u
+/* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has
+ been received. */
+#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001u
+/* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has
+ opened. */
+#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002u
+/* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder
+ stream has opened. */
+#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004u
+/* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder
+ stream has opened. */
+#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008u
+/* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has
+ received. */
+#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020u
+/* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has
+ been submitted for transmission. */
+#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040u
+
+typedef struct nghttp3_chunk {
+ nghttp3_opl_entry oplent;
+} nghttp3_chunk;
+
+nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent);
+
+struct nghttp3_conn {
+ nghttp3_objalloc out_chunk_objalloc;
+ nghttp3_objalloc stream_objalloc;
+ nghttp3_callbacks callbacks;
+ nghttp3_map streams;
+ nghttp3_qpack_decoder qdec;
+ nghttp3_qpack_encoder qenc;
+ nghttp3_pq qpack_blocked_streams;
+ struct {
+ nghttp3_pq spq;
+ } sched[NGHTTP3_URGENCY_LEVELS];
+ const nghttp3_mem *mem;
+ void *user_data;
+ int server;
+ uint16_t flags;
+
+ struct {
+ nghttp3_settings settings;
+ struct {
+ /* max_pushes is the number of push IDs that local endpoint can
+ issue. This field is used by server only and used just for
+ validation */
+ uint64_t max_pushes;
+ } uni;
+ } local;
+
+ struct {
+ struct {
+ nghttp3_idtr idtr;
+ /* max_client_streams is the cumulative number of client
+ initiated bidirectional stream ID the remote endpoint can
+ issue. This field is used on server side only. */
+ uint64_t max_client_streams;
+ } bidi;
+ nghttp3_settings settings;
+ } remote;
+
+ struct {
+ /* goaway_id is the latest ID received in GOAWAY frame. */
+ int64_t goaway_id;
+
+ int64_t max_stream_id_bidi;
+
+ /* pri_fieldbuf is a buffer to store incoming Priority Field Value
+ in PRIORITY_UPDATE frame. */
+ uint8_t pri_fieldbuf[8];
+ /* pri_fieldlen is the number of bytes written into
+ pri_fieldbuf. */
+ size_t pri_fieldbuflen;
+ } rx;
+
+ struct {
+ struct {
+ nghttp3_buf rbuf;
+ nghttp3_buf ebuf;
+ } qpack;
+ nghttp3_stream *ctrl;
+ nghttp3_stream *qenc;
+ nghttp3_stream *qdec;
+ /* goaway_id is the latest ID sent in GOAWAY frame. */
+ int64_t goaway_id;
+ } tx;
+};
+
+nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id);
+
+int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
+ int64_t stream_id);
+
+nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
+ nghttp3_stream *stream, const uint8_t *src,
+ size_t srclen, int fin);
+
+nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen, int fin);
+
+nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen);
+
+nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen);
+
+nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen);
+
+int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *data, size_t datalen);
+
+int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr);
+
+nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *data, size_t datalen,
+ int fin);
+
+int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
+ const nghttp3_frame_settings *fr);
+
+int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
+ nghttp3_stream *stream);
+
+void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn);
+
+int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
+ nghttp3_stream *stream);
+
+void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+/*
+ * nghttp3_conn_get_next_tx_stream returns next stream to send. It
+ * returns NULL if there is no such stream.
+ */
+nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn);
+
+#endif /* NGHTTP3_CONN_H */
diff --git a/lib/nghttp3_conv.c b/lib/nghttp3_conv.c
new file mode 100644
index 0000000..f7853d9
--- /dev/null
+++ b/lib/nghttp3_conv.c
@@ -0,0 +1,125 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_conv.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_str.h"
+#include "nghttp3_unreachable.h"
+
+int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) {
+ union {
+ char b[8];
+ uint16_t n16;
+ uint32_t n32;
+ uint64_t n64;
+ } n;
+
+ *plen = 1u << (*p >> 6);
+
+ switch (*plen) {
+ case 1:
+ return (int64_t)*p;
+ case 2:
+ memcpy(&n, p, 2);
+ n.b[0] &= 0x3f;
+ return (int64_t)ntohs(n.n16);
+ case 4:
+ memcpy(&n, p, 4);
+ n.b[0] &= 0x3f;
+ return (int64_t)ntohl(n.n32);
+ case 8:
+ memcpy(&n, p, 8);
+ n.b[0] &= 0x3f;
+ return (int64_t)nghttp3_ntohl64(n.n64);
+ }
+
+ nghttp3_unreachable();
+}
+
+int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; }
+
+size_t nghttp3_get_varintlen(const uint8_t *p) { return 1u << (*p >> 6); }
+
+uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n) {
+ n = nghttp3_htonl64(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n) {
+ n = htonl(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n) {
+ n = htons(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n) {
+ uint8_t *rv;
+ if (n < 64) {
+ *p++ = (uint8_t)n;
+ return p;
+ }
+ if (n < 16384) {
+ rv = nghttp3_put_uint16be(p, (uint16_t)n);
+ *p |= 0x40;
+ return rv;
+ }
+ if (n < 1073741824) {
+ rv = nghttp3_put_uint32be(p, (uint32_t)n);
+ *p |= 0x80;
+ return rv;
+ }
+ assert(n < 4611686018427387904LL);
+ rv = nghttp3_put_uint64be(p, (uint64_t)n);
+ *p |= 0xc0;
+ return rv;
+}
+
+size_t nghttp3_put_varintlen(int64_t n) {
+ if (n < 64) {
+ return 1;
+ }
+ if (n < 16384) {
+ return 2;
+ }
+ if (n < 1073741824) {
+ return 4;
+ }
+ assert(n < 4611686018427387904LL);
+ return 8;
+}
+
+uint64_t nghttp3_ord_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2) + 1;
+}
+
+uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri) {
+ return (uint8_t)((uint32_t)pri->inc << 7 | pri->urgency);
+}
diff --git a/lib/nghttp3_conv.h b/lib/nghttp3_conv.h
new file mode 100644
index 0000000..516013a
--- /dev/null
+++ b/lib/nghttp3_conv.h
@@ -0,0 +1,207 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_CONV_H
+#define NGHTTP3_CONV_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_BYTESWAP_H
+# include <byteswap.h>
+#endif /* HAVE_BYTESWAP_H */
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif /* HAVE_SYS_ENDIAN_H */
+
+#include <nghttp3/nghttp3.h>
+
+#if defined(HAVE_BSWAP_64) || \
+ (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
+# define nghttp3_bswap64 bswap_64
+#else /* !HAVE_BSWAP_64 */
+# define nghttp3_bswap64(N) \
+ ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
+#endif /* !HAVE_BSWAP_64 */
+
+#if defined(HAVE_BE64TOH) || \
+ (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
+# define nghttp3_ntohl64(N) be64toh(N)
+# define nghttp3_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+# if defined(WORDS_BIGENDIAN)
+# define nghttp3_ntohl64(N) (N)
+# define nghttp3_htonl64(N) (N)
+# else /* !WORDS_BIGENDIAN */
+# define nghttp3_ntohl64(N) nghttp3_bswap64(N)
+# define nghttp3_htonl64(N) nghttp3_bswap64(N)
+# endif /* !WORDS_BIGENDIAN */
+#endif /* !HAVE_BE64TOH */
+
+#if defined(WIN32)
+/* Windows requires ws2_32 library for ntonl family of functions. We
+ define inline functions for those functions so that we don't have
+ dependency on that lib. */
+
+# ifdef _MSC_VER
+# define STIN static __inline
+# else
+# define STIN static inline
+# endif
+
+STIN uint32_t htonl(uint32_t hostlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostlong >> 24);
+ *p++ = (hostlong >> 16) & 0xffu;
+ *p++ = (hostlong >> 8) & 0xffu;
+ *p = hostlong & 0xffu;
+ return res;
+}
+
+STIN uint16_t htons(uint16_t hostshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostshort >> 8);
+ *p = hostshort & 0xffu;
+ return res;
+}
+
+STIN uint32_t ntohl(uint32_t netlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&netlong;
+ res = *p++ << 24;
+ res += *p++ << 16;
+ res += *p++ << 8;
+ res += *p;
+ return res;
+}
+
+STIN uint16_t ntohs(uint16_t netshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&netshort;
+ res = *p++ << 8;
+ res += *p;
+ return res;
+}
+
+#endif /* WIN32 */
+
+/*
+ * nghttp3_get_varint reads variable-length integer from |p|, and
+ * returns it in host byte order. The number of bytes read is stored
+ * in |*plen|.
+ */
+int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p);
+
+/*
+ * nghttp3_get_varint_fb reads first byte of encoded variable-length
+ * integer from |p|.
+ */
+int64_t nghttp3_get_varint_fb(const uint8_t *p);
+
+/*
+ * nghttp3_get_varintlen returns the required number of bytes to read
+ * variable-length integer starting at |p|.
+ */
+size_t nghttp3_get_varintlen(const uint8_t *p);
+
+/*
+ * nghttp3_put_uint64be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n);
+
+/*
+ * nghttp3_put_uint32be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n);
+
+/*
+ * nghttp3_put_uint16be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n);
+
+/*
+ * nghttp3_put_varint writes |n| in |p| using variable-length integer
+ * encoding. It returns the one beyond of the last written position.
+ */
+uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n);
+
+/*
+ * nghttp3_put_varintlen returns the required number of bytes to
+ * encode |n|.
+ */
+size_t nghttp3_put_varintlen(int64_t n);
+
+/*
+ * nghttp3_ord_stream_id returns the ordinal number of |stream_id|.
+ */
+uint64_t nghttp3_ord_stream_id(int64_t stream_id);
+
+/*
+ * NGHTTP3_PRI_INC_MASK is a bit mask to retrieve incremental bit from
+ * a value produced by nghttp3_pri_to_uint8.
+ */
+#define NGHTTP3_PRI_INC_MASK (1 << 7)
+
+/*
+ * nghttp3_pri_to_uint8 encodes |pri| into uint8_t variable.
+ */
+uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri);
+
+/*
+ * nghttp3_pri_uint8_urgency extracts urgency from |PRI| which is
+ * supposed to be constructed by nghttp3_pri_to_uint8.
+ */
+#define nghttp3_pri_uint8_urgency(PRI) \
+ ((uint32_t)((PRI) & ~NGHTTP3_PRI_INC_MASK))
+
+/*
+ * nghttp3_pri_uint8_inc extracts inc from |PRI| which is supposed to
+ * be constructed by nghttp3_pri_to_uint8.
+ */
+#define nghttp3_pri_uint8_inc(PRI) (((PRI)&NGHTTP3_PRI_INC_MASK) != 0)
+
+#endif /* NGHTTP3_CONV_H */
diff --git a/lib/nghttp3_debug.c b/lib/nghttp3_debug.c
new file mode 100644
index 0000000..4021b0d
--- /dev/null
+++ b/lib/nghttp3_debug.c
@@ -0,0 +1,61 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_debug.h"
+
+#include <stdio.h>
+
+#ifdef DEBUGBUILD
+
+static void nghttp3_default_debug_vfprintf_callback(const char *fmt,
+ va_list args) {
+ vfprintf(stderr, fmt, args);
+}
+
+static nghttp3_debug_vprintf_callback static_debug_vprintf_callback =
+ nghttp3_default_debug_vfprintf_callback;
+
+void nghttp3_debug_vprintf(const char *format, ...) {
+ if (static_debug_vprintf_callback) {
+ va_list args;
+ va_start(args, format);
+ static_debug_vprintf_callback(format, args);
+ va_end(args);
+ }
+}
+
+void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback) {
+ static_debug_vprintf_callback = debug_vprintf_callback;
+}
+
+#else /* !DEBUGBUILD */
+
+void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback) {
+ (void)debug_vprintf_callback;
+}
+
+#endif /* !DEBUGBUILD */
diff --git a/lib/nghttp3_debug.h b/lib/nghttp3_debug.h
new file mode 100644
index 0000000..01ed918
--- /dev/null
+++ b/lib/nghttp3_debug.h
@@ -0,0 +1,44 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_DEBUG_H
+#define NGHTTP3_DEBUG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#ifdef DEBUGBUILD
+# define DEBUGF(...) nghttp3_debug_vprintf(__VA_ARGS__)
+void nghttp3_debug_vprintf(const char *format, ...);
+#else
+# define DEBUGF(...) \
+ do { \
+ } while (0)
+#endif
+
+#endif /* NGHTTP3_DEBUG_H */
diff --git a/lib/nghttp3_err.c b/lib/nghttp3_err.c
new file mode 100644
index 0000000..5cf94db
--- /dev/null
+++ b/lib/nghttp3_err.c
@@ -0,0 +1,126 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_err.h"
+
+const char *nghttp3_strerror(int liberr) {
+ switch (liberr) {
+ case NGHTTP3_ERR_INVALID_ARGUMENT:
+ return "ERR_INVALID_ARGUMENT";
+ case NGHTTP3_ERR_NOBUF:
+ return "ERR_NOBUF";
+ case NGHTTP3_ERR_INVALID_STATE:
+ return "ERR_INVALID_STATE";
+ case NGHTTP3_ERR_WOULDBLOCK:
+ return "ERR_WOULDBLOCK";
+ case NGHTTP3_ERR_STREAM_IN_USE:
+ return "ERR_STREAM_IN_USE";
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ return "ERR_MALFORMED_HTTP_HEADER";
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ return "ERR_REMOVE_HTTP_HEADER";
+ case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING:
+ return "ERR_MALFORMED_HTTP_MESSAGING";
+ case NGHTTP3_ERR_QPACK_FATAL:
+ return "ERR_QPACK_FATAL";
+ case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE:
+ return "ERR_QPACK_HEADER_TOO_LARGE";
+ case NGHTTP3_ERR_STREAM_NOT_FOUND:
+ return "ERR_STREAM_NOT_FOUND";
+ case NGHTTP3_ERR_CONN_CLOSING:
+ return "ERR_CONN_CLOSING";
+ case NGHTTP3_ERR_STREAM_DATA_OVERFLOW:
+ return "ERR_STREAM_DATA_OVERFLOW";
+ case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED:
+ return "ERR_QPACK_DECOMPRESSION_FAILED";
+ case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
+ return "ERR_QPACK_ENCODER_STREAM_ERROR";
+ case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR:
+ return "ERR_QPACK_DECODER_STREAM_ERROR";
+ case NGHTTP3_ERR_H3_FRAME_UNEXPECTED:
+ return "ERR_H3_FRAME_UNEXPECTED";
+ case NGHTTP3_ERR_H3_FRAME_ERROR:
+ return "ERR_H3_FRAME_ERROR";
+ case NGHTTP3_ERR_H3_MISSING_SETTINGS:
+ return "ERR_H3_MISSING_SETTINGS";
+ case NGHTTP3_ERR_H3_INTERNAL_ERROR:
+ return "ERR_H3_INTERNAL_ERROR";
+ case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
+ return "ERR_CLOSED_CRITICAL_STREAM";
+ case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR:
+ return "ERR_H3_GENERAL_PROTOCOL_ERROR";
+ case NGHTTP3_ERR_H3_ID_ERROR:
+ return "ERR_H3_ID_ERROR";
+ case NGHTTP3_ERR_H3_SETTINGS_ERROR:
+ return "ERR_H3_SETTINGS_ERROR";
+ case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR:
+ return "ERR_H3_STREAM_CREATION_ERROR";
+ case NGHTTP3_ERR_NOMEM:
+ return "ERR_NOMEM";
+ case NGHTTP3_ERR_CALLBACK_FAILURE:
+ return "ERR_CALLBACK_FAILURE";
+ default:
+ return "(unknown)";
+ }
+}
+
+uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) {
+ switch (liberr) {
+ case 0:
+ return NGHTTP3_H3_NO_ERROR;
+ case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED:
+ return NGHTTP3_QPACK_DECOMPRESSION_FAILED;
+ case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
+ return NGHTTP3_QPACK_ENCODER_STREAM_ERROR;
+ case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR:
+ return NGHTTP3_QPACK_DECODER_STREAM_ERROR;
+ case NGHTTP3_ERR_H3_FRAME_UNEXPECTED:
+ return NGHTTP3_H3_FRAME_UNEXPECTED;
+ case NGHTTP3_ERR_H3_FRAME_ERROR:
+ return NGHTTP3_H3_FRAME_ERROR;
+ case NGHTTP3_ERR_H3_MISSING_SETTINGS:
+ return NGHTTP3_H3_MISSING_SETTINGS;
+ case NGHTTP3_ERR_H3_INTERNAL_ERROR:
+ case NGHTTP3_ERR_NOMEM:
+ case NGHTTP3_ERR_CALLBACK_FAILURE:
+ return NGHTTP3_H3_INTERNAL_ERROR;
+ case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
+ return NGHTTP3_H3_CLOSED_CRITICAL_STREAM;
+ case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR:
+ return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
+ case NGHTTP3_ERR_H3_ID_ERROR:
+ return NGHTTP3_H3_ID_ERROR;
+ case NGHTTP3_ERR_H3_SETTINGS_ERROR:
+ return NGHTTP3_H3_SETTINGS_ERROR;
+ case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR:
+ return NGHTTP3_H3_STREAM_CREATION_ERROR;
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING:
+ return NGHTTP3_H3_MESSAGE_ERROR;
+ default:
+ return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
+ }
+}
+
+int nghttp3_err_is_fatal(int liberr) { return liberr < NGHTTP3_ERR_FATAL; }
diff --git a/lib/nghttp3_err.h b/lib/nghttp3_err.h
new file mode 100644
index 0000000..2fa914f
--- /dev/null
+++ b/lib/nghttp3_err.h
@@ -0,0 +1,34 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_ERR_H
+#define NGHTTP3_ERR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#endif /* NGHTTP3_ERR_H */
diff --git a/lib/nghttp3_frame.c b/lib/nghttp3_frame.c
new file mode 100644
index 0000000..e7d64fb
--- /dev/null
+++ b/lib/nghttp3_frame.c
@@ -0,0 +1,204 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_frame.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_conv.h"
+#include "nghttp3_str.h"
+
+uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) {
+ p = nghttp3_put_varint(p, hd->type);
+ p = nghttp3_put_varint(p, hd->length);
+ return p;
+}
+
+size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) {
+ return nghttp3_put_varintlen(hd->type) + nghttp3_put_varintlen(hd->length);
+}
+
+uint8_t *nghttp3_frame_write_settings(uint8_t *p,
+ const nghttp3_frame_settings *fr) {
+ size_t i;
+
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+
+ for (i = 0; i < fr->niv; ++i) {
+ p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id);
+ p = nghttp3_put_varint(p, (int64_t)fr->iv[i].value);
+ }
+
+ return p;
+}
+
+size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen,
+ const nghttp3_frame_settings *fr) {
+ size_t payloadlen = 0;
+ size_t i;
+
+ for (i = 0; i < fr->niv; ++i) {
+ payloadlen += nghttp3_put_varintlen((int64_t)fr->iv[i].id) +
+ nghttp3_put_varintlen((int64_t)fr->iv[i].value);
+ }
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varintlen(NGHTTP3_FRAME_SETTINGS) +
+ nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
+}
+
+uint8_t *nghttp3_frame_write_goaway(uint8_t *p,
+ const nghttp3_frame_goaway *fr) {
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+ p = nghttp3_put_varint(p, fr->id);
+
+ return p;
+}
+
+size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
+ const nghttp3_frame_goaway *fr) {
+ size_t payloadlen = nghttp3_put_varintlen(fr->id);
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varintlen(NGHTTP3_FRAME_GOAWAY) +
+ nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
+}
+
+uint8_t *
+nghttp3_frame_write_priority_update(uint8_t *p,
+ const nghttp3_frame_priority_update *fr) {
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+ p = nghttp3_put_varint(p, fr->pri_elem_id);
+
+ assert(fr->pri.urgency <= NGHTTP3_URGENCY_LOW);
+
+ *p++ = 'u';
+ *p++ = '=';
+ *p++ = (uint8_t)('0' + fr->pri.urgency);
+
+ if (fr->pri.inc) {
+#define NGHTTP3_PRIORITY_INCREMENTAL ", i"
+ p = nghttp3_cpymem(p, (const uint8_t *)NGHTTP3_PRIORITY_INCREMENTAL,
+ sizeof(NGHTTP3_PRIORITY_INCREMENTAL) - 1);
+ }
+
+ return p;
+}
+
+size_t nghttp3_frame_write_priority_update_len(
+ int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr) {
+ size_t payloadlen = nghttp3_put_varintlen(fr->pri_elem_id) + sizeof("u=U") -
+ 1 + (fr->pri.inc ? sizeof(", i") - 1 : 0);
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varintlen(fr->hd.type) +
+ nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
+}
+
+int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_mem *mem) {
+ size_t i;
+ uint8_t *data = NULL;
+ size_t buflen = 0;
+ nghttp3_nv *p;
+
+ if (nvlen == 0) {
+ *pnva = NULL;
+
+ return 0;
+ }
+
+ for (i = 0; i < nvlen; ++i) {
+ /* + 1 for null-termination */
+ if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) == 0) {
+ buflen += nva[i].namelen + 1;
+ }
+ if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) == 0) {
+ buflen += nva[i].valuelen + 1;
+ }
+ }
+
+ buflen += sizeof(nghttp3_nv) * nvlen;
+
+ *pnva = nghttp3_mem_malloc(mem, buflen);
+
+ if (*pnva == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ p = *pnva;
+ data = (uint8_t *)(*pnva) + sizeof(nghttp3_nv) * nvlen;
+
+ for (i = 0; i < nvlen; ++i) {
+ p->flags = nva[i].flags;
+
+ if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) {
+ p->name = nva[i].name;
+ p->namelen = nva[i].namelen;
+ } else {
+ if (nva[i].namelen) {
+ memcpy(data, nva[i].name, nva[i].namelen);
+ }
+ p->name = data;
+ p->namelen = nva[i].namelen;
+ data[p->namelen] = '\0';
+ nghttp3_downcase(p->name, p->namelen);
+ data += nva[i].namelen + 1;
+ }
+
+ if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) {
+ p->value = nva[i].value;
+ p->valuelen = nva[i].valuelen;
+ } else {
+ if (nva[i].valuelen) {
+ memcpy(data, nva[i].value, nva[i].valuelen);
+ }
+ p->value = data;
+ p->valuelen = nva[i].valuelen;
+ data[p->valuelen] = '\0';
+ data += nva[i].valuelen + 1;
+ }
+
+ ++p;
+ }
+ return 0;
+}
+
+void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, nva);
+}
+
+void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
+ const nghttp3_mem *mem) {
+ if (fr == NULL) {
+ return;
+ }
+
+ nghttp3_nva_del(fr->nva, mem);
+}
diff --git a/lib/nghttp3_frame.h b/lib/nghttp3_frame.h
new file mode 100644
index 0000000..4ee161d
--- /dev/null
+++ b/lib/nghttp3_frame.h
@@ -0,0 +1,214 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_FRAME_H
+#define NGHTTP3_FRAME_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_buf.h"
+
+typedef enum nghttp3_frame_type {
+ NGHTTP3_FRAME_DATA = 0x00,
+ NGHTTP3_FRAME_HEADERS = 0x01,
+ NGHTTP3_FRAME_CANCEL_PUSH = 0x03,
+ NGHTTP3_FRAME_SETTINGS = 0x04,
+ NGHTTP3_FRAME_PUSH_PROMISE = 0x05,
+ NGHTTP3_FRAME_GOAWAY = 0x07,
+ NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d,
+ /* PRIORITY_UPDATE: https://datatracker.ietf.org/doc/html/rfc9218 */
+ NGHTTP3_FRAME_PRIORITY_UPDATE = 0x0f0700,
+ NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID = 0x0f0701,
+} nghttp3_frame_type;
+
+typedef enum nghttp3_h2_reserved_type {
+ NGHTTP3_H2_FRAME_PRIORITY = 0x02,
+ NGHTTP3_H2_FRAME_PING = 0x06,
+ NGHTTP3_H2_FRAME_WINDOW_UPDATE = 0x08,
+ NGHTTP3_H2_FRAME_CONTINUATION = 0x9,
+} nghttp3_h2_reserved_type;
+
+typedef struct nghttp3_frame_hd {
+ int64_t type;
+ int64_t length;
+} nghttp3_frame_hd;
+
+typedef struct nghttp3_frame_data {
+ nghttp3_frame_hd hd;
+} nghttp3_frame_data;
+
+typedef struct nghttp3_frame_headers {
+ nghttp3_frame_hd hd;
+ nghttp3_nv *nva;
+ size_t nvlen;
+} nghttp3_frame_headers;
+
+#define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06
+#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01
+#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07
+#define NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL 0x08
+
+#define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2
+#define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3
+#define NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE 0x4
+#define NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE 0x5
+
+typedef struct nghttp3_settings_entry {
+ uint64_t id;
+ uint64_t value;
+} nghttp3_settings_entry;
+
+typedef struct nghttp3_frame_settings {
+ nghttp3_frame_hd hd;
+ size_t niv;
+ nghttp3_settings_entry iv[1];
+} nghttp3_frame_settings;
+
+typedef struct nghttp3_frame_goaway {
+ nghttp3_frame_hd hd;
+ int64_t id;
+} nghttp3_frame_goaway;
+
+typedef struct nghttp3_frame_priority_update {
+ nghttp3_frame_hd hd;
+ /* pri_elem_id is stream ID if hd.type ==
+ NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type ==
+ NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined
+ otherwise. */
+ int64_t pri_elem_id;
+ nghttp3_pri pri;
+} nghttp3_frame_priority_update;
+
+typedef union nghttp3_frame {
+ nghttp3_frame_hd hd;
+ nghttp3_frame_data data;
+ nghttp3_frame_headers headers;
+ nghttp3_frame_settings settings;
+ nghttp3_frame_goaway goaway;
+ nghttp3_frame_priority_update priority_update;
+} nghttp3_frame;
+
+/*
+ * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This
+ * function assumes that |dest| has enough space to write |hd|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd);
+
+/*
+ * nghttp3_frame_write_hd_len returns the number of bytes required to
+ * write |hd|. hd->length must be set.
+ */
+size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd);
+
+/*
+ * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|.
+ * This function assumes that |dest| has enough space to write |fr|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_settings(uint8_t *dest,
+ const nghttp3_frame_settings *fr);
+
+/*
+ * nghttp3_frame_write_settings_len returns the number of bytes
+ * required to write |fr|. fr->hd.length is ignored. This function
+ * stores payload length in |*ppayloadlen|.
+ */
+size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen,
+ const nghttp3_frame_settings *fr);
+
+/*
+ * nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|.
+ * This function assumes that |dest| has enough space to write |fr|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_goaway(uint8_t *dest,
+ const nghttp3_frame_goaway *fr);
+
+/*
+ * nghttp3_frame_write_goaway_len returns the number of bytes required
+ * to write |fr|. fr->hd.length is ignored. This function stores
+ * payload length in |*ppayloadlen|.
+ */
+size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
+ const nghttp3_frame_goaway *fr);
+
+/*
+ * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame
+ * |fr| to |dest|. This function assumes that |dest| has enough space
+ * to write |fr|.
+ *
+ * This function returns |dest| plus the number of bytes written;
+ */
+uint8_t *
+nghttp3_frame_write_priority_update(uint8_t *dest,
+ const nghttp3_frame_priority_update *fr);
+
+/*
+ * nghttp3_frame_write_priority_update_len returns the number of bytes
+ * required to write |fr|. fr->hd.length is ignored. This function
+ * stores payload length in |*ppayloadlen|.
+ */
+size_t nghttp3_frame_write_priority_update_len(
+ int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr);
+
+/*
+ * nghttp3_nva_copy copies name/value pairs from |nva|, which contains
+ * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so
+ * that all items can be stored. The resultant name and value in
+ * nghttp2_nv are guaranteed to be NULL-terminated even if the input
+ * is not null-terminated.
+ *
+ * The |*pnva| must be freed using nghttp3_nva_del().
+ *
+ * This function returns 0 if it succeeds or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_nva_del frees |nva|.
+ */
+void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_frame_headers_free frees memory allocated for |fr|. It
+ * assumes that fr->nva is created by nghttp3_nva_copy() or NULL.
+ */
+void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
+ const nghttp3_mem *mem);
+
+#endif /* NGHTTP3_FRAME_H */
diff --git a/lib/nghttp3_gaptr.c b/lib/nghttp3_gaptr.c
new file mode 100644
index 0000000..88cb49a
--- /dev/null
+++ b/lib/nghttp3_gaptr.c
@@ -0,0 +1,169 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_gaptr.h"
+
+#include <string.h>
+#include <assert.h>
+
+void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) {
+ nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range),
+ mem);
+
+ gaptr->mem = mem;
+}
+
+static int gaptr_gap_init(nghttp3_gaptr *gaptr) {
+ nghttp3_range range = {0, UINT64_MAX};
+ int rv;
+
+ rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) {
+ if (gaptr == NULL) {
+ return;
+ }
+
+ nghttp3_ksl_free(&gaptr->gap);
+}
+
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen) {
+ int rv;
+ nghttp3_range k, m, l, r, q = {offset, offset + datalen};
+ nghttp3_ksl_it it;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ rv = gaptr_gap_init(gaptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+
+ for (; !nghttp3_ksl_it_end(&it);) {
+ k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+ m = nghttp3_range_intersect(&q, &k);
+ if (!nghttp3_range_len(&m)) {
+ break;
+ }
+
+ if (nghttp3_range_eq(&k, &m)) {
+ nghttp3_ksl_remove_hint(&gaptr->gap, &it, &it, &k);
+ continue;
+ }
+ nghttp3_range_cut(&l, &r, &k, &m);
+ if (nghttp3_range_len(&l)) {
+ nghttp3_ksl_update_key(&gaptr->gap, &k, &l);
+
+ if (nghttp3_range_len(&r)) {
+ rv = nghttp3_ksl_insert(&gaptr->gap, &it, &r, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else if (nghttp3_range_len(&r)) {
+ nghttp3_ksl_update_key(&gaptr->gap, &k, &r);
+ }
+ nghttp3_ksl_it_next(&it);
+ }
+ return 0;
+}
+
+uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) {
+ nghttp3_ksl_it it;
+ nghttp3_range r;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = nghttp3_ksl_begin(&gaptr->gap);
+ r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+
+ return r.begin;
+}
+
+nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
+ uint64_t offset) {
+ nghttp3_range q = {offset, offset + 1};
+ nghttp3_ksl_it it;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ nghttp3_range r = {0, UINT64_MAX};
+ return r;
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+
+ assert(!nghttp3_ksl_it_end(&it));
+
+ return *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+}
+
+int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen) {
+ nghttp3_range q = {offset, offset + datalen};
+ nghttp3_ksl_it it;
+ nghttp3_range k;
+ nghttp3_range m;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+ k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+ m = nghttp3_range_intersect(&q, &k);
+
+ return nghttp3_range_len(&m) == 0;
+}
+
+void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) {
+ nghttp3_ksl_it it;
+ nghttp3_range r;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return;
+ }
+
+ it = nghttp3_ksl_begin(&gaptr->gap);
+
+ assert(!nghttp3_ksl_it_end(&it));
+
+ r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+
+ nghttp3_ksl_remove_hint(&gaptr->gap, NULL, &it, &r);
+}
diff --git a/lib/nghttp3_gaptr.h b/lib/nghttp3_gaptr.h
new file mode 100644
index 0000000..7c83c84
--- /dev/null
+++ b/lib/nghttp3_gaptr.h
@@ -0,0 +1,99 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_GAPTR_H
+#define NGHTTP3_GAPTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_ksl.h"
+#include "nghttp3_range.h"
+
+/*
+ * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX).
+ */
+typedef struct nghttp3_gaptr {
+ /* gap maintains the range of offset which is not received
+ yet. Initially, its range is [0, UINT64_MAX). */
+ nghttp3_ksl gap;
+ /* mem is custom memory allocator */
+ const nghttp3_mem *mem;
+} nghttp3_gaptr;
+
+/*
+ * nghttp3_gaptr_init initializes |gaptr|.
+ */
+void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_gaptr_free frees resources allocated for |gaptr|.
+ */
+void nghttp3_gaptr_free(nghttp3_gaptr *gaptr);
+
+/*
+ * nghttp3_gaptr_push adds new data of length |datalen| at the stream
+ * offset |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory
+ */
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen);
+
+/*
+ * nghttp3_gaptr_first_gap_offset returns the offset to the first gap.
+ * If there is no gap, it returns UINT64_MAX.
+ */
+uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr);
+
+/*
+ * nghttp3_gaptr_get_first_gap_after returns the first gap which
+ * overlaps or comes after |offset|.
+ */
+nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
+ uint64_t offset);
+
+/*
+ * nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset +
+ * datalen) is completely pushed into this object.
+ */
+int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen);
+
+/*
+ * nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if
+ * the range is pushed. This function assumes that at least one gap
+ * exists.
+ */
+void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr);
+
+#endif /* NGHTTP3_GAPTR_H */
diff --git a/lib/nghttp3_http.c b/lib/nghttp3_http.c
new file mode 100644
index 0000000..29af976
--- /dev/null
+++ b/lib/nghttp3_http.c
@@ -0,0 +1,1654 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2015 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_http.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_stream.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_conv.h"
+#include "nghttp3_unreachable.h"
+
+static uint8_t downcase(uint8_t c) {
+ return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
+}
+
+/*
+ * memieq returns 1 if the data pointed by |a| of length |n| equals to
+ * |b| of the same length in case-insensitive manner. The data
+ * pointed by |a| must not include upper cased letters (A-Z).
+ */
+static int memieq(const void *a, const void *b, size_t n) {
+ size_t i;
+ const uint8_t *aa = a, *bb = b;
+
+ for (i = 0; i < n; ++i) {
+ if (aa[i] != downcase(bb[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
+
+static int64_t parse_uint(const uint8_t *s, size_t len) {
+ int64_t n = 0;
+ size_t i;
+ if (len == 0) {
+ return -1;
+ }
+ for (i = 0; i < len; ++i) {
+ if ('0' <= s[i] && s[i] <= '9') {
+ if (n > INT64_MAX / 10) {
+ return -1;
+ }
+ n *= 10;
+ if (n > INT64_MAX - (s[i] - '0')) {
+ return -1;
+ }
+ n += s[i] - '0';
+ continue;
+ }
+ return -1;
+ }
+ return n;
+}
+
+static int check_pseudo_header(nghttp3_http_state *http,
+ const nghttp3_qpack_nv *nv, uint32_t flag) {
+ if ((http->flags & flag) || nv->value->len == 0) {
+ return 0;
+ }
+ http->flags |= flag;
+ return 1;
+}
+
+static int expect_response_body(nghttp3_http_state *http) {
+ return (http->flags & NGHTTP3_HTTP_FLAG_METH_HEAD) == 0 &&
+ http->status_code / 100 != 1 && http->status_code != 304 &&
+ http->status_code != 204;
+}
+
+/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
+ header field to represent system-wide OPTIONS request. Otherwise,
+ :path header field value must start with "/". This function must
+ be called after ":method" header field was received. This function
+ returns nonzero if path is valid.*/
+static int check_path_flags(nghttp3_http_state *http) {
+ return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 ||
+ ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) ||
+ ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) &&
+ (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK)));
+}
+
+/* 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 nghttp3_ssize 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 nghttp3_ssize sf_parse_integer_or_decimal(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ int sign = 1;
+ int64_t value = 0;
+ int type = NGHTTP3_SF_VALUE_TYPE_INTEGER;
+ size_t len = 0;
+ size_t fpos = 0;
+ size_t i;
+
+ if (*p == '-') {
+ if (++p == end) {
+ return -1;
+ }
+
+ sign = -1;
+ }
+
+ if (*p < '0' || '9' < *p) {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case '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 != NGHTTP3_SF_VALUE_TYPE_INTEGER) {
+ goto fin;
+ }
+
+ if (len > 12) {
+ return -1;
+ }
+ fpos = len;
+ type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ break;
+ default:
+ goto fin;
+ };
+ }
+
+fin:
+ switch (type) {
+ case NGHTTP3_SF_VALUE_TYPE_INTEGER:
+ if (dest) {
+ dest->type = (uint8_t)type;
+ dest->i = value * sign;
+ }
+
+ return p - begin;
+ case NGHTTP3_SF_VALUE_TYPE_DECIMAL:
+ if (fpos == len || len - fpos > 3) {
+ return -1;
+ }
+
+ if (dest) {
+ dest->type = (uint8_t)type;
+ dest->d = (double)value;
+ for (i = len - fpos; i > 0; --i) {
+ dest->d /= (double)10;
+ }
+ dest->d *= sign;
+ }
+
+ return p - begin;
+ default:
+ nghttp3_unreachable();
+ }
+}
+
+/* Generated by genchartbl.py */
+static const int SF_DQUOTE_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */,
+ 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */,
+ 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */,
+ 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */,
+ 1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_string(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if (*p++ != '"') {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case '\\':
+ if (++p == end) {
+ return -1;
+ }
+
+ switch (*p) {
+ case '"':
+ case '\\':
+ break;
+ default:
+ return -1;
+ }
+
+ break;
+ case '"':
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_STRING;
+ dest->s.base = begin + 1;
+ dest->s.len = (size_t)(p - dest->s.base);
+ }
+
+ ++p;
+
+ 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 nghttp3_ssize sf_parse_token(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') {
+ return -1;
+ }
+
+ for (; p != end && SF_TOKEN_CHARS[*p]; ++p)
+ ;
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_TOKEN;
+ dest->s.base = begin;
+ dest->s.len = (size_t)(p - begin);
+ }
+
+ return p - begin;
+}
+
+/* Generated by genchartbl.py */
+static const int SF_BYTESEQ_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
+ 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
+ 0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */,
+ 0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
+ 0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
+ 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_byteseq(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if (*p++ != ':') {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case ':':
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_BYTESEQ;
+ dest->s.base = begin + 1;
+ dest->s.len = (size_t)(p - dest->s.base);
+ }
+
+ ++p;
+
+ return p - begin;
+ default:
+ if (!SF_BYTESEQ_CHARS[*p]) {
+ return -1;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static nghttp3_ssize sf_parse_boolean(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ int b;
+
+ if (*p++ != '?') {
+ return -1;
+ }
+
+ if (p == end) {
+ return -1;
+ }
+
+ switch (*p++) {
+ case '0':
+ b = 0;
+ break;
+ case '1':
+ b = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN;
+ dest->b = b;
+ }
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_bare_item(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ switch (*begin) {
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return sf_parse_integer_or_decimal(dest, begin, end);
+ case '"':
+ return sf_parse_string(dest, begin, end);
+ case '*':
+ return sf_parse_token(dest, begin, end);
+ case ':':
+ return sf_parse_byteseq(dest, begin, end);
+ case '?':
+ return sf_parse_boolean(dest, begin, end);
+ default:
+ if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) {
+ return sf_parse_token(dest, begin, end);
+ }
+ return -1;
+ }
+}
+
+#define sf_discard_sp_end_err(BEGIN, END, ERR) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ return (ERR); \
+ } \
+ if (*(BEGIN) != ' ') { \
+ break; \
+ } \
+ }
+
+static nghttp3_ssize sf_parse_params(const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ for (; p != end && *p == ';';) {
+ ++p;
+
+ sf_discard_sp_end_err(p, end, -1);
+
+ slen = sf_parse_key(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (p == end || *p != '=') {
+ /* Boolean true */
+ } else if (++p == end) {
+ return -1;
+ } else {
+ slen = sf_parse_bare_item(NULL, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+ }
+ }
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_item(nghttp3_sf_value *dest, const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ slen = sf_parse_bare_item(dest, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ return p - begin;
+}
+
+nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ return sf_parse_item(dest, begin, end);
+}
+
+static nghttp3_ssize sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ if (*p++ != '(') {
+ return -1;
+ }
+
+ for (;;) {
+ sf_discard_sp_end_err(p, end, -1);
+
+ if (*p == ')') {
+ ++p;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_INNER_LIST;
+ }
+
+ return p - begin;
+ }
+
+ slen = sf_parse_item(NULL, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (p == end || (*p != ' ' && *p != ')')) {
+ return -1;
+ }
+ }
+}
+
+nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ return sf_parse_inner_list(dest, begin, end);
+}
+
+static nghttp3_ssize sf_parse_item_or_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ if (*begin == '(') {
+ return sf_parse_inner_list(dest, begin, end);
+ }
+
+ return sf_parse_item(dest, begin, end);
+}
+
+#define sf_discard_ows(BEGIN, END) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ goto fin; \
+ } \
+ if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
+ break; \
+ } \
+ }
+
+#define sf_discard_ows_end_err(BEGIN, END, ERR) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ return (ERR); \
+ } \
+ if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
+ break; \
+ } \
+ }
+
+int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
+ size_t valuelen) {
+ const uint8_t *p = value, *end = value + valuelen;
+ nghttp3_ssize slen;
+ nghttp3_sf_value val;
+ nghttp3_pri pri = *dest;
+ const uint8_t *key;
+ size_t keylen;
+
+ for (; p != end && *p == ' '; ++p)
+ ;
+
+ for (; p != end;) {
+ slen = sf_parse_key(p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ key = p;
+ keylen = (size_t)slen;
+
+ p += slen;
+
+ if (p == end || *p != '=') {
+ /* Boolean true */
+ val.type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN;
+ val.b = 1;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ } else if (++p == end) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ } else {
+ slen = sf_parse_item_or_inner_list(&val, p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ }
+
+ p += slen;
+
+ if (keylen == 1) {
+ switch (key[0]) {
+ case 'i':
+ if (val.type != NGHTTP3_SF_VALUE_TYPE_BOOLEAN) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.inc = val.b;
+
+ break;
+ case 'u':
+ if (val.type != NGHTTP3_SF_VALUE_TYPE_INTEGER ||
+ val.i < NGHTTP3_URGENCY_HIGH || NGHTTP3_URGENCY_LOW < val.i) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.urgency = (uint32_t)val.i;
+
+ break;
+ }
+ }
+
+ sf_discard_ows(p, end);
+
+ if (*p++ != ',') {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ sf_discard_ows_end_err(p, end, NGHTTP3_ERR_INVALID_ARGUMENT);
+ }
+
+fin:
+ *dest = pri;
+
+ return 0;
+}
+
+static int http_request_on_header(nghttp3_http_state *http,
+ nghttp3_qpack_nv *nv, int trailers,
+ int connect_protocol) {
+ nghttp3_pri pri;
+
+ if (nv->name->base[0] == ':') {
+ if (trailers ||
+ (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__AUTHORITY:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__METHOD:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__METHOD)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ switch (nv->value->len) {
+ case 4:
+ if (lstreq("HEAD", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
+ }
+ break;
+ case 7:
+ switch (nv->value->base[6]) {
+ case 'T':
+ if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
+ }
+ break;
+ case 'S':
+ if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_OPTIONS;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PATH)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (nv->value->base[0] == '/') {
+ http->flags |= NGHTTP3_HTTP_FLAG_PATH_REGULAR;
+ } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
+ http->flags |= NGHTTP3_HTTP_FLAG_PATH_ASTERISK;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__SCHEME:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* scheme is case-insensitive:
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 */
+ if (lstrieq("http", nv->value->base, nv->value->len) ||
+ lstrieq("https", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ if (!connect_protocol) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
+ /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
+ MUST NOT generate a trailer that contains a field necessary for
+ message framing (e.g., Transfer-Encoding and Content-Length),
+ ... */
+ if (trailers) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->content_length != -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = parse_uint(nv->value->base, nv->value->len);
+ if (http->content_length == -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ /* disallowed header fields */
+ case NGHTTP3_QPACK_TOKEN_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
+ case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
+ case NGHTTP3_QPACK_TOKEN_UPGRADE:
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ if (!trailers && !(http->flags & NGHTTP3_HTTP_FLAG_BAD_PRIORITY)) {
+ pri.urgency = nghttp3_pri_uint8_urgency(http->pri);
+ pri.inc = nghttp3_pri_uint8_inc(http->pri);
+ if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) ==
+ 0) {
+ http->pri = nghttp3_pri_to_uint8(&pri);
+ http->flags |= NGHTTP3_HTTP_FLAG_PRIORITY;
+ } else {
+ http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY;
+ http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY;
+ }
+ }
+ break;
+ default:
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+static int http_response_on_header(nghttp3_http_state *http,
+ nghttp3_qpack_nv *nv, int trailers) {
+ if (nv->name->base[0] == ':') {
+ if (trailers ||
+ (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__STATUS: {
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (nv->value->len != 3) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
+ if (http->status_code < 100 || http->status_code == 101) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
+ /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
+ MUST NOT generate a trailer that contains a field necessary for
+ message framing (e.g., Transfer-Encoding and Content-Length),
+ ... */
+ if (trailers) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->status_code == 204) {
+ /* content-length header field in 204 response is prohibited by
+ RFC 7230. But some widely used servers send content-length:
+ 0. Until they get fixed, we ignore it. */
+ if (http->content_length != -1) {
+ /* Found multiple content-length field */
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (!lstrieq("0", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = 0;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->status_code / 100 == 1) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
+ if (http->status_code / 100 == 2 &&
+ (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->content_length != -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = parse_uint(nv->value->base, nv->value->len);
+ if (http->content_length == -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ /* disallowed header fields */
+ case NGHTTP3_QPACK_TOKEN_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
+ case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
+ case NGHTTP3_QPACK_TOKEN_UPGRADE:
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ default:
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+/* Generated by genauthroitychartbl.py */
+static char VALID_AUTHORITY_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */,
+ 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
+ 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */,
+ 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
+ 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
+ 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
+ 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
+ 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
+ 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
+ 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
+ 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
+ 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
+ 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
+ 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
+ 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
+ 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
+ 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
+ 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
+ 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
+ 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
+ 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
+ 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
+ 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
+ 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
+ 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
+ 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
+ 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
+ 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
+ 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
+ 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
+};
+
+static int check_authority(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_AUTHORITY_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int check_scheme(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+
+ if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
+ return 0;
+ }
+
+ last = value + len;
+ ++value;
+
+ for (; value != last; ++value) {
+ if (!(('A' <= *value && *value <= 'Z') ||
+ ('a' <= *value && *value <= 'z') ||
+ ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
+ *value == '.')) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genmethodchartbl.py */
+static char VALID_METHOD_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
+ 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
+ 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */,
+ 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
+ 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
+ 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
+ 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
+ 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
+ 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
+ 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
+ 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
+ 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
+ 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
+ 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
+ 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
+ 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
+ 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
+ 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
+ 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
+ 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
+ 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
+ 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
+ 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
+ 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
+ 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
+ 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
+ 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
+ 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
+ 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
+ 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
+};
+
+static int check_method(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_METHOD_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genpathchartbl.py */
+static char VALID_PATH_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
+ 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
+ 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
+ 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
+ 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
+ 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
+ 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
+ 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
+ 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
+ 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
+ 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
+ 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
+ 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
+ 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
+ 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
+ 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
+ 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
+ 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
+ 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
+ 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
+ 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
+ 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
+ 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
+ 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
+ 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
+ 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
+ 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
+ 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
+ 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
+ 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
+ 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
+ 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
+ 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
+ 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
+ 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
+};
+
+static int check_path(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_PATH_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv,
+ int request, int trailers, int connect_protocol) {
+ int rv;
+ size_t i;
+ uint8_t c;
+
+ if (!nghttp3_check_header_name(nv->name->base, nv->name->len)) {
+ if (nv->name->len > 0 && nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* header field name must be lower-cased without exception */
+ for (i = 0; i < nv->name->len; ++i) {
+ c = nv->name->base[i];
+ if ('A' <= c && c <= 'Z') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+ /* When ignoring regular header fields, we set this flag so that
+ we still enforce header field ordering rule for pseudo header
+ fields. */
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+
+ assert(nv->name->len > 0);
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__METHOD:
+ rv = check_method(nv->value->base, nv->value->len);
+ break;
+ case NGHTTP3_QPACK_TOKEN__SCHEME:
+ rv = check_scheme(nv->value->base, nv->value->len);
+ break;
+ case NGHTTP3_QPACK_TOKEN__AUTHORITY:
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ if (request) {
+ rv = check_authority(nv->value->base, nv->value->len);
+ } else {
+ /* The use of host field in response field section is
+ undefined. */
+ rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ rv = check_path(nv->value->base, nv->value->len);
+ break;
+ default:
+ rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
+ }
+
+ if (rv == 0) {
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* When ignoring regular header fields, we set this flag so that
+ we still enforce header field ordering rule for pseudo header
+ fields. */
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+
+ if (request) {
+ rv = http_request_on_header(http, nv, trailers, connect_protocol);
+ } else {
+ rv = http_response_on_header(http, nv, trailers);
+ }
+
+ if (nv->name->base[0] != ':') {
+ switch (rv) {
+ case 0:
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+int nghttp3_http_on_request_headers(nghttp3_http_state *http) {
+ if (!(http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
+ (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
+ if ((http->flags & (NGHTTP3_HTTP_FLAG__SCHEME | NGHTTP3_HTTP_FLAG__PATH)) ||
+ (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = -1;
+ } else {
+ if ((http->flags & NGHTTP3_HTTP_FLAG_REQ_HEADERS) !=
+ NGHTTP3_HTTP_FLAG_REQ_HEADERS ||
+ (http->flags &
+ (NGHTTP3_HTTP_FLAG__AUTHORITY | NGHTTP3_HTTP_FLAG_HOST)) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if ((http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
+ ((http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) == 0 ||
+ (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (!check_path_flags(http)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_response_headers(nghttp3_http_state *http) {
+ if ((http->flags & NGHTTP3_HTTP_FLAG__STATUS) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+
+ if (http->status_code / 100 == 1) {
+ /* non-final response */
+ http->flags = (http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) |
+ NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
+ http->content_length = -1;
+ http->status_code = -1;
+ return 0;
+ }
+
+ http->flags &= ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
+
+ if (!expect_response_body(http)) {
+ http->content_length = 0;
+ } else if (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
+ http->content_length = -1;
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) {
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
+ (stream->rx.http.content_length != -1 &&
+ stream->rx.http.content_length != stream->rx.http.recv_content_length)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n) {
+ stream->rx.http.recv_content_length += (int64_t)n;
+
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
+ (stream->rx.http.content_length != -1 &&
+ stream->rx.http.recv_content_length > stream->rx.http.content_length)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+
+ return 0;
+}
+
+void nghttp3_http_record_request_method(nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen) {
+ size_t i;
+ const nghttp3_nv *nv;
+
+ /* TODO we should do this strictly. */
+ for (i = 0; i < nvlen; ++i) {
+ nv = &nva[i];
+ if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
+ memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
+ continue;
+ }
+ if (lstreq("CONNECT", nv->value, nv->valuelen)) {
+ stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
+ return;
+ }
+ if (lstreq("HEAD", nv->value, nv->valuelen)) {
+ stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
+ return;
+ }
+ return;
+ }
+}
+
+/* Generated by gennmchartbl.py */
+static const int VALID_HD_NAME_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
+ 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
+ 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */,
+ 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */,
+ 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */,
+ 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
+ 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */,
+ 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */,
+ 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */,
+ 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
+ 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
+ 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
+ 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
+ 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
+ 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
+ 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
+ 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
+ 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
+ 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
+ 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
+ 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
+ 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
+ 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
+ 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
+ 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
+ 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
+ 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
+ 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
+ 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
+ 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
+ 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
+ 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
+ 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
+ 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
+ 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
+ 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
+};
+
+int nghttp3_check_header_name(const uint8_t *name, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+ if (*name == ':') {
+ if (len == 1) {
+ return 0;
+ }
+ ++name;
+ --len;
+ }
+ for (last = name + len; name != last; ++name) {
+ if (!VALID_HD_NAME_CHARS[*name]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genvchartbl.py */
+static const int VALID_HD_VALUE_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
+ 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
+ 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
+ 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
+ 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
+ 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
+ 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
+ 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
+ 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
+ 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
+ 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
+ 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
+ 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
+ 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
+ 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
+ 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
+ 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
+ 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
+ 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
+ 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
+ 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
+ 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
+ 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
+ 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
+ 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
+ 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
+ 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
+ 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
+ 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
+ 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
+ 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
+ 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
+ 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
+ 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
+ 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
+};
+
+int nghttp3_check_header_value(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+
+ switch (len) {
+ case 0:
+ return 1;
+ case 1:
+ return !(*value == ' ' || *value == '\t');
+ default:
+ if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' ||
+ *(value + len - 1) == '\t') {
+ return 0;
+ }
+ }
+
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_HD_VALUE_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/lib/nghttp3_http.h b/lib/nghttp3_http.h
new file mode 100644
index 0000000..1617348
--- /dev/null
+++ b/lib/nghttp3_http.h
@@ -0,0 +1,202 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2015 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_HTTP_H
+#define NGHTTP3_HTTP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_stream nghttp3_stream;
+
+typedef struct nghttp3_http_state nghttp3_http_state;
+
+/* HTTP related flags to enforce HTTP semantics */
+
+/* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_HTTP_FLAG_NONE 0x00u
+/* header field seen so far */
+#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01u
+#define NGHTTP3_HTTP_FLAG__PATH 0x02u
+#define NGHTTP3_HTTP_FLAG__METHOD 0x04u
+#define NGHTTP3_HTTP_FLAG__SCHEME 0x08u
+/* host is not pseudo header, but we require either host or
+ :authority */
+#define NGHTTP3_HTTP_FLAG_HOST 0x10u
+#define NGHTTP3_HTTP_FLAG__STATUS 0x20u
+/* required header fields for HTTP request except for CONNECT
+ method. */
+#define NGHTTP3_HTTP_FLAG_REQ_HEADERS \
+ (NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH | \
+ NGHTTP3_HTTP_FLAG__SCHEME)
+#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40u
+/* HTTP method flags */
+#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80u
+#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100u
+#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200u
+#define NGHTTP3_HTTP_FLAG_METH_ALL \
+ (NGHTTP3_HTTP_FLAG_METH_CONNECT | NGHTTP3_HTTP_FLAG_METH_HEAD | \
+ NGHTTP3_HTTP_FLAG_METH_OPTIONS)
+/* :path category */
+/* path starts with "/" */
+#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400u
+/* path "*" */
+#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800u
+/* scheme */
+/* "http" or "https" scheme */
+#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000u
+/* set if final response is expected */
+#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000u
+/* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header
+ field is seen. */
+#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000u
+/* NGHTTP3_HTTP_FLAG_PRIORITY is set when priority header field is
+ processed. */
+#define NGHTTP3_HTTP_FLAG_PRIORITY 0x8000u
+/* NGHTTP3_HTTP_FLAG_BAD_PRIORITY is set when an error is encountered
+ while parsing priority header field. */
+#define NGHTTP3_HTTP_FLAG_BAD_PRIORITY 0x010000u
+
+/*
+ * This function is called when HTTP header field |nv| received for
+ * |http|. This function will validate |nv| against the current state
+ * of stream. Pass nonzero if this is request headers. Pass nonzero
+ * to |trailers| if |nv| is included in trailers. |connect_protocol|
+ * is nonzero if Extended CONNECT Method is enabled.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Invalid HTTP header field was received.
+ * NGHTTP3_ERR_REMOVE_HTTP_HEADER
+ * Invalid HTTP header field was received but it can be treated as
+ * if it was not received because of compatibility reasons.
+ */
+int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv,
+ int request, int trailers, int connect_protocol);
+
+/*
+ * This function is called when request header is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Required HTTP header field was not received; or an invalid
+ * header field was received.
+ */
+int nghttp3_http_on_request_headers(nghttp3_http_state *http);
+
+/*
+ * This function is called when response header is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Required HTTP header field was not received; or an invalid
+ * header field was received.
+ */
+int nghttp3_http_on_response_headers(nghttp3_http_state *http);
+
+/*
+ * This function is called when read side stream is closed. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING
+ * HTTP messaging is violated.
+ */
+int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream);
+
+/*
+ * This function is called when chunk of data is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING
+ * HTTP messaging is violated.
+ */
+int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n);
+
+/*
+ * This function inspects header fields in |nva| of length |nvlen| and
+ * records its method in stream->http_flags.
+ */
+void nghttp3_http_record_request_method(nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen);
+
+/*
+ * RFC 8941 Structured Field Values.
+ */
+typedef enum nghttp3_sf_value_type {
+ NGHTTP3_SF_VALUE_TYPE_BOOLEAN,
+ NGHTTP3_SF_VALUE_TYPE_INTEGER,
+ NGHTTP3_SF_VALUE_TYPE_DECIMAL,
+ NGHTTP3_SF_VALUE_TYPE_STRING,
+ NGHTTP3_SF_VALUE_TYPE_TOKEN,
+ NGHTTP3_SF_VALUE_TYPE_BYTESEQ,
+ NGHTTP3_SF_VALUE_TYPE_INNER_LIST,
+} nghttp3_sf_value_type;
+
+/*
+ * nghttp3_sf_value stores Structured Field Values item. For Inner
+ * List, only type is set to NGHTTP3_SF_VALUE_TYPE_INNER_LIST.
+ */
+typedef struct nghttp3_sf_value {
+ uint8_t type;
+ union {
+ int b;
+ int64_t i;
+ double d;
+ struct {
+ const uint8_t *base;
+ size_t len;
+ } s;
+ };
+} nghttp3_sf_value;
+
+/*
+ * nghttp3_sf_parse_item parses the input sequence [|begin|, |end|)
+ * and stores the parsed an Item in |dest|. It returns the number of
+ * bytes consumed if it succeeds, or -1. This function is declared
+ * here for unit tests.
+ */
+nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end);
+
+/*
+ * nghttp3_sf_parse_inner_list parses the input sequence [|begin|, |end|)
+ * and stores the parsed an Inner List in |dest|. It returns the number of
+ * bytes consumed if it succeeds, or -1. This function is declared
+ * here for unit tests.
+ */
+nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end);
+
+#endif /* NGHTTP3_HTTP_H */
diff --git a/lib/nghttp3_idtr.c b/lib/nghttp3_idtr.c
new file mode 100644
index 0000000..dc34841
--- /dev/null
+++ b/lib/nghttp3_idtr.c
@@ -0,0 +1,80 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_idtr.h"
+
+#include <assert.h>
+
+void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) {
+ nghttp3_gaptr_init(&idtr->gap, mem);
+
+ idtr->server = server;
+}
+
+void nghttp3_idtr_free(nghttp3_idtr *idtr) {
+ if (idtr == NULL) {
+ return;
+ }
+
+ nghttp3_gaptr_free(&idtr->gap);
+}
+
+/*
+ * id_from_stream_id translates |stream_id| to id space used by
+ * nghttp3_idtr.
+ */
+static uint64_t id_from_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2);
+}
+
+int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ if (nghttp3_gaptr_is_pushed(&idtr->gap, q, 1)) {
+ return NGHTTP3_ERR_STREAM_IN_USE;
+ }
+
+ return nghttp3_gaptr_push(&idtr->gap, q, 1);
+}
+
+int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ return nghttp3_gaptr_is_pushed(&idtr->gap, q, 1);
+}
+
+uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr) {
+ return nghttp3_gaptr_first_gap_offset(&idtr->gap);
+}
diff --git a/lib/nghttp3_idtr.h b/lib/nghttp3_idtr.h
new file mode 100644
index 0000000..ea3346c
--- /dev/null
+++ b/lib/nghttp3_idtr.h
@@ -0,0 +1,90 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_IDTR_H
+#define NGHTTP3_IDTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_gaptr.h"
+
+/*
+ * nghttp3_idtr tracks the usage of stream ID.
+ */
+typedef struct nghttp3_idtr {
+ /* gap maintains the range of ID which is not used yet. Initially,
+ its range is [0, UINT64_MAX). */
+ nghttp3_gaptr gap;
+ /* server is nonzero if this object records server initiated stream
+ ID. */
+ int server;
+} nghttp3_idtr;
+
+/*
+ * nghttp3_idtr_init initializes |idtr|.
+ *
+ * If this object records server initiated ID (even number), set
+ * |server| to nonzero.
+ */
+void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_idtr_free frees resources allocated for |idtr|.
+ */
+void nghttp3_idtr_free(nghttp3_idtr *idtr);
+
+/*
+ * nghttp3_idtr_open claims that |stream_id| is in used.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_STREAM_IN_USE
+ * ID has already been used.
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id);
+
+/*
+ * nghttp3_idtr_open tells whether ID |stream_id| is in used or not.
+ *
+ * It returns nonzero if |stream_id| is used.
+ */
+int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id);
+
+/*
+ * nghttp3_idtr_first_gap returns the first id of first gap. If there
+ * is no gap, it returns UINT64_MAX. The returned id is an id space
+ * used in this object internally, and not stream ID.
+ */
+uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr);
+
+#endif /* NGHTTP3_IDTR_H */
diff --git a/lib/nghttp3_ksl.c b/lib/nghttp3_ksl.c
new file mode 100644
index 0000000..adea677
--- /dev/null
+++ b/lib/nghttp3_ksl.c
@@ -0,0 +1,827 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_ksl.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_macro.h"
+#include "nghttp3_mem.h"
+#include "nghttp3_range.h"
+
+static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
+
+static size_t ksl_nodelen(size_t keylen) {
+ return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) &
+ ~(uintptr_t)0xfu;
+}
+
+static size_t ksl_blklen(size_t nodelen) {
+ return sizeof(nghttp3_ksl_blk) + nodelen * NGHTTP3_KSL_MAX_NBLK -
+ sizeof(uint64_t);
+}
+
+/*
+ * ksl_node_set_key sets |key| to |node|.
+ */
+static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node,
+ const void *key) {
+ memcpy(node->key, key, ksl->keylen);
+}
+
+void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
+ size_t keylen, const nghttp3_mem *mem) {
+ size_t nodelen = ksl_nodelen(keylen);
+
+ nghttp3_objalloc_init(&ksl->blkalloc,
+ ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8,
+ mem);
+
+ ksl->head = NULL;
+ ksl->front = ksl->back = NULL;
+ ksl->compar = compar;
+ ksl->keylen = keylen;
+ ksl->nodelen = nodelen;
+ ksl->n = 0;
+}
+
+static nghttp3_ksl_blk *ksl_blk_objalloc_new(nghttp3_ksl *ksl) {
+ return nghttp3_objalloc_ksl_blk_len_get(&ksl->blkalloc,
+ ksl_blklen(ksl->nodelen));
+}
+
+static void ksl_blk_objalloc_del(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ nghttp3_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
+}
+
+static int ksl_head_init(nghttp3_ksl *ksl) {
+ nghttp3_ksl_blk *head = ksl_blk_objalloc_new(ksl);
+ if (!head) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ head->next = head->prev = NULL;
+ head->n = 0;
+ head->leaf = 1;
+
+ ksl->head = head;
+ ksl->front = ksl->back = head;
+
+ return 0;
+}
+
+#ifdef NOMEMPOOL
+/*
+ * ksl_free_blk frees |blk| recursively.
+ */
+static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ size_t i;
+
+ if (!blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk);
+ }
+ }
+
+ ksl_blk_objalloc_del(ksl, blk);
+}
+#endif /* NOMEMPOOL */
+
+void nghttp3_ksl_free(nghttp3_ksl *ksl) {
+ if (!ksl || !ksl->head) {
+ return;
+ }
+
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ nghttp3_objalloc_free(&ksl->blkalloc);
+}
+
+/*
+ * ksl_split_blk splits |blk| into 2 nghttp3_ksl_blk objects. The new
+ * nghttp3_ksl_blk is always the "right" block.
+ *
+ * It returns the pointer to the nghttp3_ksl_blk created which is the
+ * located at the right of |blk|, or NULL which indicates out of
+ * memory error.
+ */
+static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ nghttp3_ksl_blk *rblk;
+
+ rblk = ksl_blk_objalloc_new(ksl);
+ if (rblk == NULL) {
+ return NULL;
+ }
+
+ rblk->next = blk->next;
+ blk->next = rblk;
+ if (rblk->next) {
+ rblk->next->prev = rblk;
+ } else if (ksl->back == blk) {
+ ksl->back = rblk;
+ }
+ rblk->prev = blk;
+ rblk->leaf = blk->leaf;
+
+ rblk->n = blk->n / 2;
+
+ memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n),
+ ksl->nodelen * rblk->n);
+
+ blk->n -= rblk->n;
+
+ assert(blk->n >= NGHTTP3_KSL_MIN_NBLK);
+ assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK);
+
+ return rblk;
+}
+
+/*
+ * ksl_split_node splits a node included in |blk| at the position |i|
+ * into 2 adjacent nodes. The new node is always inserted at the
+ * position |i+1|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *node;
+ nghttp3_ksl_blk *lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk, *rblk;
+
+ rblk = ksl_split_blk(ksl, lblk);
+ if (rblk == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ memmove(blk->nodes + (i + 2) * ksl->nodelen,
+ blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+ node->blk = rblk;
+ ++blk->n;
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+
+ return 0;
+}
+
+/*
+ * ksl_split_head splits a head (root) block. It increases the height
+ * of skip list by 1.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_head(nghttp3_ksl *ksl) {
+ nghttp3_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
+ nghttp3_ksl_node *node;
+
+ rblk = ksl_split_blk(ksl, ksl->head);
+ if (rblk == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ lblk = ksl->head;
+
+ nhead = ksl_blk_objalloc_new(ksl);
+ if (nhead == NULL) {
+ ksl_blk_objalloc_del(ksl, rblk);
+ return NGHTTP3_ERR_NOMEM;
+ }
+ nhead->next = nhead->prev = NULL;
+ nhead->n = 2;
+ nhead->leaf = 0;
+
+ node = nghttp3_ksl_nth_node(ksl, nhead, 0);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ node->blk = lblk;
+
+ node = nghttp3_ksl_nth_node(ksl, nhead, 1);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+ node->blk = rblk;
+
+ ksl->head = nhead;
+
+ return 0;
+}
+
+/*
+ * insert_node inserts a node whose key is |key| with the associated
+ * |data| at the index of |i|. This function assumes that the number
+ * of nodes contained by |blk| is strictly less than
+ * NGHTTP3_KSL_MAX_NBLK.
+ */
+static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i,
+ const nghttp3_ksl_key *key, void *data) {
+ nghttp3_ksl_node *node;
+
+ assert(blk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
+ ksl->nodelen * (blk->n - i));
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, key);
+ node->data = data;
+
+ ++blk->n;
+}
+
+static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar) {
+ size_t i;
+ nghttp3_ksl_node *node;
+
+ for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes;
+ i < blk->n && compar((nghttp3_ksl_key *)node->key, key);
+ ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
+ ;
+
+ return i;
+}
+
+int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key, void *data) {
+ nghttp3_ksl_blk *blk;
+ nghttp3_ksl_node *node;
+ size_t i;
+ int rv;
+
+ if (!ksl->head) {
+ rv = ksl_head_init(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ blk = ksl->head;
+
+ if (blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_head(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ blk = ksl->head;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i < blk->n &&
+ !ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ ksl_insert_node(ksl, blk, i, key, data);
+ ++ksl->n;
+ if (it) {
+ nghttp3_ksl_it_init(it, ksl, blk, i);
+ }
+ return 0;
+ }
+
+ if (i == blk->n) {
+ /* This insertion extends the largest key in this subtree. */
+ for (; !blk->leaf;) {
+ node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1);
+ if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, blk->n - 1);
+ if (rv != 0) {
+ return rv;
+ }
+ node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1);
+ }
+ ksl_node_set_key(ksl, node, key);
+ blk = node->blk;
+ }
+ ksl_insert_node(ksl, blk, blk->n, key, data);
+ ++ksl->n;
+ if (it) {
+ nghttp3_ksl_it_init(it, ksl, blk, blk->n - 1);
+ }
+ return 0;
+ }
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, i);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ksl->compar((nghttp3_ksl_key *)node->key, key)) {
+ node = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+ if (ksl->compar((nghttp3_ksl_key *)node->key, key)) {
+ ksl_node_set_key(ksl, node, key);
+ }
+ }
+ }
+
+ blk = node->blk;
+ }
+}
+
+/*
+ * ksl_remove_node removes the node included in |blk| at the index of
+ * |i|.
+ */
+static void ksl_remove_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ --blk->n;
+}
+
+/*
+ * ksl_merge_node merges 2 nodes which are the nodes at the index of
+ * |i| and |i + 1|.
+ *
+ * If |blk| is the direct descendant of head (root) block and the head
+ * block contains just 2 nodes, the merged block becomes head block,
+ * which decreases the height of |ksl| by 1.
+ *
+ * This function returns the pointer to the merged block.
+ */
+static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
+ size_t i) {
+ nghttp3_ksl_blk *lblk, *rblk;
+
+ assert(i + 1 < blk->n);
+
+ lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ rblk = nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk;
+
+ assert(lblk->n + rblk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
+ ksl->nodelen * rblk->n);
+
+ lblk->n += rblk->n;
+ lblk->next = rblk->next;
+ if (lblk->next) {
+ lblk->next->prev = lblk;
+ } else if (ksl->back == rblk) {
+ ksl->back = lblk;
+ }
+
+ ksl_blk_objalloc_del(ksl, rblk);
+
+ if (ksl->head == blk && blk->n == 2) {
+ ksl_blk_objalloc_del(ksl, ksl->head);
+ ksl->head = lblk;
+ } else {
+ ksl_remove_node(ksl, blk, i + 1);
+ ksl_node_set_key(ksl, nghttp3_ksl_nth_node(ksl, blk, i),
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ }
+
+ return lblk;
+}
+
+/*
+ * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i - 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible.
+ */
+static void ksl_shift_left(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i > 0);
+
+ lnode = nghttp3_ksl_nth_node(ksl, blk, i - 1);
+ rnode = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ assert(lnode->blk->n < NGHTTP3_KSL_MAX_NBLK);
+ assert(rnode->blk->n > NGHTTP3_KSL_MIN_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - lnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n);
+ assert(rnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n);
+
+ memcpy(lnode->blk->nodes + ksl->nodelen * lnode->blk->n, rnode->blk->nodes,
+ ksl->nodelen * n);
+
+ lnode->blk->n += (uint32_t)n;
+ rnode->blk->n -= (uint32_t)n;
+
+ ksl_node_set_key(
+ ksl, lnode,
+ nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+
+ memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen * n,
+ ksl->nodelen * rnode->blk->n);
+}
+
+/*
+ * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i + 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible..
+ */
+static void ksl_shift_right(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i < blk->n - 1);
+
+ lnode = nghttp3_ksl_nth_node(ksl, blk, i);
+ rnode = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+
+ assert(lnode->blk->n > NGHTTP3_KSL_MIN_NBLK);
+ assert(rnode->blk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - rnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n);
+ assert(rnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n);
+
+ memmove(rnode->blk->nodes + ksl->nodelen * n, rnode->blk->nodes,
+ ksl->nodelen * rnode->blk->n);
+
+ rnode->blk->n += (uint32_t)n;
+ lnode->blk->n -= (uint32_t)n;
+
+ memcpy(rnode->blk->nodes, lnode->blk->nodes + ksl->nodelen * lnode->blk->n,
+ ksl->nodelen * n);
+
+ ksl_node_set_key(
+ ksl, lnode,
+ nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+}
+
+/*
+ * key_equal returns nonzero if |lhs| and |rhs| are equal using the
+ * function |compar|.
+ */
+static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ return !compar(lhs, rhs) && !compar(rhs, lhs);
+}
+
+int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_it *hint,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = hint->blk;
+
+ assert(ksl->head);
+
+ if (blk->n <= NGHTTP3_KSL_MIN_NBLK) {
+ return nghttp3_ksl_remove(ksl, it, key);
+ }
+
+ ksl_remove_node(ksl, blk, hint->i);
+
+ --ksl->n;
+
+ if (it) {
+ if (hint->i == blk->n && blk->next) {
+ nghttp3_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ nghttp3_ksl_it_init(it, ksl, blk, hint->i);
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_node *node;
+ size_t i;
+
+ if (!ksl->head) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!blk->leaf && blk->n == 2 &&
+ nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK &&
+ nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) {
+ blk = ksl_merge_node(ksl, ksl->head, 0);
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (i == blk->n) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (blk->leaf) {
+ if (ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ ksl_remove_node(ksl, blk, i);
+ --ksl->n;
+ if (it) {
+ if (blk->n == i && blk->next) {
+ nghttp3_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ nghttp3_ksl_it_init(it, ksl, blk, i);
+ }
+ }
+ return 0;
+ }
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ blk = node->blk;
+ continue;
+ }
+
+ assert(node->blk->n == NGHTTP3_KSL_MIN_NBLK);
+
+ if (i + 1 < blk->n &&
+ nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ ksl_shift_left(ksl, blk, i + 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i > 0 &&
+ nghttp3_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ ksl_shift_right(ksl, blk, i - 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i + 1 < blk->n) {
+ blk = ksl_merge_node(ksl, blk, i);
+ continue;
+ }
+
+ assert(i > 0);
+
+ blk = ksl_merge_node(ksl, blk, i - 1);
+ }
+}
+
+nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_it it;
+ size_t i;
+
+ if (!blk) {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_it it;
+ size_t i;
+
+ if (!blk) {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
+ const nghttp3_ksl_key *new_key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_node *node;
+ size_t i;
+
+ assert(ksl->head);
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
+
+ assert(i < blk->n);
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (blk->leaf) {
+ assert(key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key));
+ ksl_node_set_key(ksl, node, new_key);
+ return;
+ }
+
+ if (key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key) ||
+ ksl->compar((nghttp3_ksl_key *)node->key, new_key)) {
+ ksl_node_set_key(ksl, node, new_key);
+ }
+
+ blk = node->blk;
+ }
+}
+
+static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) {
+ size_t i;
+ nghttp3_ksl_node *node;
+
+ fprintf(stderr, "LV=%zu n=%u\n", level, blk->n);
+
+ if (blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key);
+ }
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ for (i = 0; i < blk->n; ++i) {
+ ksl_print(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk, level + 1);
+ }
+}
+
+size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; }
+
+void nghttp3_ksl_clear(nghttp3_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ ksl->front = ksl->back = ksl->head = NULL;
+ ksl->n = 0;
+
+ nghttp3_objalloc_clear(&ksl->blkalloc);
+}
+
+void nghttp3_ksl_print(nghttp3_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+ ksl_print(ksl, ksl->head, 0);
+}
+
+nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) {
+ nghttp3_ksl_it it;
+
+ if (ksl->head) {
+ nghttp3_ksl_it_init(&it, ksl, ksl->front, 0);
+ } else {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
+ return it;
+}
+
+nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) {
+ nghttp3_ksl_it it;
+
+ if (ksl->head) {
+ nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+ } else {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
+ return it;
+}
+
+void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
+ nghttp3_ksl_blk *blk, size_t i) {
+ it->ksl = ksl;
+ it->blk = blk;
+ it->i = i;
+}
+
+void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) {
+ assert(!nghttp3_ksl_it_begin(it));
+
+ if (it->i == 0) {
+ it->blk = it->blk->prev;
+ it->i = it->blk->n - 1;
+ } else {
+ --it->i;
+ }
+}
+
+int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) {
+ return it->i == 0 && it->blk->prev == NULL;
+}
+
+int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_range *a = lhs, *b = rhs;
+ return a->begin < b->begin;
+}
+
+int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_range *a = lhs, *b = rhs;
+ return a->begin < b->begin &&
+ !(nghttp3_max(a->begin, b->begin) < nghttp3_min(a->end, b->end));
+}
diff --git a/lib/nghttp3_ksl.h b/lib/nghttp3_ksl.h
new file mode 100644
index 0000000..0bc10e8
--- /dev/null
+++ b/lib/nghttp3_ksl.h
@@ -0,0 +1,348 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_KSL_H
+#define NGHTTP3_KSL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_objalloc.h"
+
+/*
+ * Skip List using single key instead of range.
+ */
+
+#define NGHTTP3_KSL_DEGR 16
+/* NGHTTP3_KSL_MAX_NBLK is the maximum number of nodes which a single
+ block can contain. */
+#define NGHTTP3_KSL_MAX_NBLK (2 * NGHTTP3_KSL_DEGR - 1)
+/* NGHTTP3_KSL_MIN_NBLK is the minimum number of nodes which a single
+ block other than root must contains. */
+#define NGHTTP3_KSL_MIN_NBLK (NGHTTP3_KSL_DEGR - 1)
+
+/*
+ * nghttp3_ksl_key represents key in nghttp3_ksl.
+ */
+typedef void nghttp3_ksl_key;
+
+typedef struct nghttp3_ksl_node nghttp3_ksl_node;
+
+typedef struct nghttp3_ksl_blk nghttp3_ksl_blk;
+
+/*
+ * nghttp3_ksl_node is a node which contains either nghttp3_ksl_blk or
+ * opaque data. If a node is an internal node, it contains
+ * nghttp3_ksl_blk. Otherwise, it has data. The key is stored at the
+ * location starting at key.
+ */
+struct nghttp3_ksl_node {
+ union {
+ nghttp3_ksl_blk *blk;
+ void *data;
+ };
+ union {
+ uint64_t align;
+ /* key is a buffer to include key associated to this node.
+ Because the length of key is unknown until nghttp3_ksl_init is
+ called, the actual buffer will be allocated after this
+ field. */
+ uint8_t key[1];
+ };
+};
+
+/*
+ * nghttp3_ksl_blk contains nghttp3_ksl_node objects.
+ */
+struct nghttp3_ksl_blk {
+ union {
+ struct {
+ /* next points to the next block if leaf field is nonzero. */
+ nghttp3_ksl_blk *next;
+ /* prev points to the previous block if leaf field is
+ nonzero. */
+ nghttp3_ksl_blk *prev;
+ /* n is the number of nodes this object contains in nodes. */
+ uint32_t n;
+ /* leaf is nonzero if this block contains leaf nodes. */
+ uint32_t leaf;
+ union {
+ uint64_t align;
+ /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK
+ nghttp3_ksl_node objects. Because nghttp3_ksl_node object
+ is allocated along with the additional variable length key
+ storage, the size of buffer is unknown until
+ nghttp3_ksl_init is called. */
+ uint8_t nodes[1];
+ };
+ };
+
+ nghttp3_opl_entry oplent;
+ };
+};
+
+nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent);
+
+/*
+ * nghttp3_ksl_compar is a function type which returns nonzero if key
+ * |lhs| should be placed before |rhs|. It returns 0 otherwise.
+ */
+typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+typedef struct nghttp3_ksl nghttp3_ksl;
+
+typedef struct nghttp3_ksl_it nghttp3_ksl_it;
+
+/*
+ * nghttp3_ksl_it is a forward iterator to iterate nodes.
+ */
+struct nghttp3_ksl_it {
+ const nghttp3_ksl *ksl;
+ nghttp3_ksl_blk *blk;
+ size_t i;
+};
+
+/*
+ * nghttp3_ksl is a deterministic paged skip list.
+ */
+struct nghttp3_ksl {
+ nghttp3_objalloc blkalloc;
+ /* head points to the root block. */
+ nghttp3_ksl_blk *head;
+ /* front points to the first leaf block. */
+ nghttp3_ksl_blk *front;
+ /* back points to the last leaf block. */
+ nghttp3_ksl_blk *back;
+ nghttp3_ksl_compar compar;
+ size_t n;
+ /* keylen is the size of key */
+ size_t keylen;
+ /* nodelen is the actual size of nghttp3_ksl_node including key
+ storage. */
+ size_t nodelen;
+};
+
+/*
+ * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare
+ * function. |keylen| is the length of key.
+ */
+void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
+ size_t keylen, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is
+ * NULL, this function does nothing. It does not free the memory
+ * region pointed by |ksl| itself.
+ */
+void nghttp3_ksl_free(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_insert inserts |key| with its associated |data|. On
+ * successful insertion, the iterator points to the inserted node is
+ * stored in |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * |key| already exists.
+ */
+int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key, void *data);
+
+/*
+ * nghttp3_ksl_remove removes the |key| from |ksl|.
+ *
+ * This function assigns the iterator to |*it|, which points to the
+ * node which is located at the right next of the removed node if |it|
+ * is not NULL. If |key| is not found, no deletion takes place and
+ * the return value of nghttp3_ksl_end(ksl) is assigned to |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * |key| does not exist.
+ */
+int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key);
+
+/*
+ * nghttp3_ksl_remove_hint removes the |key| from |ksl|. |hint| must
+ * point to the same node denoted by |key|. |hint| is used to remove
+ * a node efficiently in some cases. Other than that, it behaves
+ * exactly like nghttp3_ksl_remove. |it| and |hint| can point to the
+ * same object.
+ */
+int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_it *hint,
+ const nghttp3_ksl_key *key);
+
+/*
+ * nghttp3_ksl_lower_bound returns the iterator which points to the
+ * first node which has the key which is equal to |key| or the last
+ * node which satisfies !compar(&node->key, key). If there is no such
+ * node, it returns the iterator which satisfies nghttp3_ksl_it_end(it)
+ * != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key);
+
+/*
+ * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound,
+ * but it takes custom function |compar| to do lower bound search.
+ */
+nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar);
+
+/*
+ * nghttp3_ksl_update_key replaces the key of nodes which has |old_key|
+ * with |new_key|. |new_key| must be strictly greater than the
+ * previous node and strictly smaller than the next node.
+ */
+void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
+ const nghttp3_ksl_key *new_key);
+
+/*
+ * nghttp3_ksl_begin returns the iterator which points to the first
+ * node. If there is no node in |ksl|, it returns the iterator which
+ * satisfies nghttp3_ksl_it_end(it) != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_end returns the iterator which points to the node
+ * following the last node. The returned object satisfies
+ * nghttp3_ksl_it_end(). If there is no node in |ksl|, it returns the
+ * iterator which satisfies nghttp3_ksl_it_begin(it) != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_len returns the number of elements stored in |ksl|.
+ */
+size_t nghttp3_ksl_len(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_clear removes all elements stored in |ksl|.
+ */
+void nghttp3_ksl_clear(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_nth_node returns the |n|th node under |blk|.
+ */
+#define nghttp3_ksl_nth_node(KSL, BLK, N) \
+ ((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
+
+/*
+ * nghttp3_ksl_print prints its internal state in stderr. It assumes
+ * that the key is of type int64_t. This function should be used for
+ * the debugging purpose only.
+ */
+void nghttp3_ksl_print(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_it_init initializes |it|.
+ */
+void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
+ nghttp3_ksl_blk *blk, size_t i);
+
+/*
+ * nghttp3_ksl_it_get returns the data associated to the node which
+ * |it| points to. It is undefined to call this function when
+ * nghttp3_ksl_it_end(it) returns nonzero.
+ */
+#define nghttp3_ksl_it_get(IT) \
+ nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
+
+/*
+ * nghttp3_ksl_it_next advances the iterator by one. It is undefined
+ * if this function is called when nghttp3_ksl_it_end(it) returns
+ * nonzero.
+ */
+#define nghttp3_ksl_it_next(IT) \
+ (++(IT)->i == (IT)->blk->n && (IT)->blk->next \
+ ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
+ : 0)
+
+/*
+ * nghttp3_ksl_it_prev moves backward the iterator by one. It is
+ * undefined if this function is called when nghttp3_ksl_it_begin(it)
+ * returns nonzero.
+ */
+void nghttp3_ksl_it_prev(nghttp3_ksl_it *it);
+
+/*
+ * nghttp3_ksl_it_end returns nonzero if |it| points to the beyond the
+ * last node.
+ */
+#define nghttp3_ksl_it_end(IT) \
+ ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
+
+/*
+ * nghttp3_ksl_it_begin returns nonzero if |it| points to the first
+ * node. |it| might satisfy both nghttp3_ksl_it_begin(&it) and
+ * nghttp3_ksl_it_end(&it) if the skip list has no node.
+ */
+int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it);
+
+/*
+ * nghttp3_ksl_key returns the key of the node which |it| points to.
+ * It is undefined to call this function when nghttp3_ksl_it_end(it)
+ * returns nonzero.
+ */
+#define nghttp3_ksl_it_key(IT) \
+ ((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
+
+/*
+ * nghttp3_ksl_range_compar is an implementation of
+ * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * nghttp3_range object and the function returns nonzero if (const
+ * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
+ * *)(rhs->ptr)->begin.
+ */
+int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+/*
+ * nghttp3_ksl_range_exclusive_compar is an implementation of
+ * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * nghttp3_range object and the function returns nonzero if (const
+ * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
+ * *)(rhs->ptr)->begin and the 2 ranges do not intersect.
+ */
+int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+#endif /* NGHTTP3_KSL_H */
diff --git a/lib/nghttp3_macro.h b/lib/nghttp3_macro.h
new file mode 100644
index 0000000..a44e907
--- /dev/null
+++ b/lib/nghttp3_macro.h
@@ -0,0 +1,51 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MACRO_H
+#define NGHTTP3_MACRO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stddef.h>
+
+#include <nghttp3/nghttp3.h>
+
+#define nghttp3_min(A, B) ((A) < (B) ? (A) : (B))
+#define nghttp3_max(A, B) ((A) > (B) ? (A) : (B))
+
+#define nghttp3_struct_of(ptr, type, member) \
+ ((type *)(void *)((char *)(ptr)-offsetof(type, member)))
+
+#define nghttp3_arraylen(A) (sizeof(A) / sizeof(*(A)))
+
+#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
+
+/* NGHTTP3_MAX_VARINT` is the maximum value which can be encoded in
+ variable-length integer encoding. */
+#define NGHTTP3_MAX_VARINT ((1ULL << 62) - 1)
+
+#endif /* NGHTTP3_MACRO_H */
diff --git a/lib/nghttp3_map.c b/lib/nghttp3_map.c
new file mode 100644
index 0000000..fcfc31a
--- /dev/null
+++ b/lib/nghttp3_map.c
@@ -0,0 +1,337 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_map.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+
+#define NGHTTP3_INITIAL_TABLE_LENBITS 4
+
+void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) {
+ map->mem = mem;
+ map->tablelen = 0;
+ map->tablelenbits = 0;
+ map->table = NULL;
+ map->size = 0;
+}
+
+void nghttp3_map_free(nghttp3_map *map) {
+ if (!map) {
+ return;
+ }
+
+ nghttp3_mem_free(map->mem, map->table);
+}
+
+void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr) {
+ uint32_t i;
+ nghttp3_map_bucket *bkt;
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ continue;
+ }
+
+ func(bkt->data, ptr);
+ }
+}
+
+int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr) {
+ int rv;
+ uint32_t i;
+ nghttp3_map_bucket *bkt;
+
+ if (map->size == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ continue;
+ }
+
+ rv = func(bkt->data, ptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t hash(nghttp3_map_key_type key) {
+ return (uint32_t)((key * 11400714819323198485llu) >> 32);
+}
+
+static size_t h2idx(uint32_t hash, uint32_t bits) {
+ return hash >> (32 - bits);
+}
+
+static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
+ nghttp3_map_bucket *bkt, size_t idx) {
+ return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
+}
+
+static void map_bucket_swap(nghttp3_map_bucket *bkt, uint32_t *phash,
+ nghttp3_map_key_type *pkey, void **pdata) {
+ uint32_t h = bkt->hash;
+ nghttp3_map_key_type key = bkt->key;
+ void *data = bkt->data;
+
+ bkt->hash = *phash;
+ bkt->key = *pkey;
+ bkt->data = *pdata;
+
+ *phash = h;
+ *pkey = key;
+ *pdata = data;
+}
+
+static void map_bucket_set_data(nghttp3_map_bucket *bkt, uint32_t hash,
+ nghttp3_map_key_type key, void *data) {
+ bkt->hash = hash;
+ bkt->key = key;
+ bkt->data = data;
+}
+
+void nghttp3_map_print_distance(nghttp3_map *map) {
+ uint32_t i;
+ size_t idx;
+ nghttp3_map_bucket *bkt;
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ fprintf(stderr, "@%u <EMPTY>\n", i);
+ continue;
+ }
+
+ idx = h2idx(bkt->hash, map->tablelenbits);
+ fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i,
+ bkt->hash, bkt->key, idx,
+ distance(map->tablelen, map->tablelenbits, bkt, idx));
+ }
+}
+
+static int insert(nghttp3_map_bucket *table, uint32_t tablelen,
+ uint32_t tablelenbits, uint32_t hash,
+ nghttp3_map_key_type key, void *data) {
+ size_t idx = h2idx(hash, tablelenbits);
+ size_t d = 0, dd;
+ nghttp3_map_bucket *bkt;
+
+ for (;;) {
+ bkt = &table[idx];
+
+ if (bkt->data == NULL) {
+ map_bucket_set_data(bkt, hash, key, data);
+ return 0;
+ }
+
+ dd = distance(tablelen, tablelenbits, bkt, idx);
+ if (d > dd) {
+ map_bucket_swap(bkt, &hash, &key, &data);
+ d = dd;
+ } else if (bkt->key == key) {
+ /* TODO This check is just a waste after first swap or if this
+ function is called from map_resize. That said, there is no
+ difference with or without this conditional in performance
+ wise. */
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ ++d;
+ idx = (idx + 1) & (tablelen - 1);
+ }
+}
+
+/* new_tablelen must be power of 2 and new_tablelen == (1 <<
+ new_tablelenbits) must hold. */
+static int map_resize(nghttp3_map *map, uint32_t new_tablelen,
+ uint32_t new_tablelenbits) {
+ uint32_t i;
+ nghttp3_map_bucket *new_table;
+ nghttp3_map_bucket *bkt;
+ int rv;
+ (void)rv;
+
+ new_table =
+ nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket));
+ if (new_table == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+ if (bkt->data == NULL) {
+ continue;
+ }
+ rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
+ bkt->data);
+
+ assert(0 == rv);
+ }
+
+ nghttp3_mem_free(map->mem, map->table);
+ map->tablelen = new_tablelen;
+ map->tablelenbits = new_tablelenbits;
+ map->table = new_table;
+
+ return 0;
+}
+
+int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) {
+ int rv;
+
+ assert(data);
+
+ /* Load factor is 0.75 */
+ if ((map->size + 1) * 4 > map->tablelen * 3) {
+ if (map->tablelen) {
+ rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ rv = map_resize(map, 1 << NGHTTP3_INITIAL_TABLE_LENBITS,
+ NGHTTP3_INITIAL_TABLE_LENBITS);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
+ data);
+ if (rv != 0) {
+ return rv;
+ }
+ ++map->size;
+ return 0;
+}
+
+void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key) {
+ uint32_t h;
+ size_t idx;
+ nghttp3_map_bucket *bkt;
+ size_t d = 0;
+
+ if (map->size == 0) {
+ return NULL;
+ }
+
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NULL;
+ }
+
+ if (bkt->key == key) {
+ return bkt->data;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) {
+ uint32_t h;
+ size_t idx, didx;
+ nghttp3_map_bucket *bkt;
+ size_t d = 0;
+
+ if (map->size == 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (bkt->key == key) {
+ map_bucket_set_data(bkt, 0, 0, NULL);
+
+ didx = idx;
+ idx = (idx + 1) & (map->tablelen - 1);
+
+ for (;;) {
+ bkt = &map->table[idx];
+ if (bkt->data == NULL ||
+ distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
+ break;
+ }
+
+ map->table[didx] = *bkt;
+ map_bucket_set_data(bkt, 0, 0, NULL);
+ didx = idx;
+
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+
+ --map->size;
+
+ return 0;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+void nghttp3_map_clear(nghttp3_map *map) {
+ if (map->tablelen == 0) {
+ return;
+ }
+
+ memset(map->table, 0, sizeof(*map->table) * map->tablelen);
+ map->size = 0;
+}
+
+size_t nghttp3_map_size(nghttp3_map *map) { return map->size; }
diff --git a/lib/nghttp3_map.h b/lib/nghttp3_map.h
new file mode 100644
index 0000000..79dff02
--- /dev/null
+++ b/lib/nghttp3_map.h
@@ -0,0 +1,137 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MAP_H
+#define NGHTTP3_MAP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+/* Implementation of unordered map */
+
+typedef uint64_t nghttp3_map_key_type;
+
+typedef struct nghttp3_map_bucket {
+ uint32_t hash;
+ nghttp3_map_key_type key;
+ void *data;
+} nghttp3_map_bucket;
+
+typedef struct nghttp3_map {
+ nghttp3_map_bucket *table;
+ const nghttp3_mem *mem;
+ size_t size;
+ uint32_t tablelen;
+ uint32_t tablelenbits;
+} nghttp3_map;
+
+/*
+ * Initializes the map |map|.
+ */
+void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |map|. The stored entries
+ * are not freed by this function. Use nghttp3_map_each_free() to free
+ * each entries.
+ */
+void nghttp3_map_free(nghttp3_map *map);
+
+/*
+ * Deallocates each entries using |func| function and any resources
+ * allocated for |map|. The |func| function is responsible for freeing
+ * given the |data| object. The |ptr| will be passed to the |func| as
+ * send argument. The return value of the |func| will be ignored.
+ */
+void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr);
+
+/*
+ * Inserts the new |data| with the |key| to the map |map|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * The item associated by |key| already exists.
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory
+ */
+int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data);
+
+/*
+ * Returns the data associated by the key |key|. If there is no such
+ * data, this function returns NULL.
+ */
+void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key);
+
+/*
+ * Removes the data associated by the key |key| from the |map|. The
+ * removed data is not freed by this function.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * The data associated by |key| does not exist.
+ */
+int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key);
+
+/*
+ * Removes all entries from |map|.
+ */
+void nghttp3_map_clear(nghttp3_map *map);
+
+/*
+ * Returns the number of items stored in the map |map|.
+ */
+size_t nghttp3_map_size(nghttp3_map *map);
+
+/*
+ * Applies the function |func| to each data in the |map| with the
+ * optional user supplied pointer |ptr|.
+ *
+ * If the |func| returns 0, this function calls the |func| with the
+ * next data. If the |func| returns nonzero, it will not call the
+ * |func| for further entries and return the return value of the
+ * |func| immediately. Thus, this function returns 0 if all the
+ * invocations of the |func| return 0, or nonzero value which the last
+ * invocation of |func| returns.
+ *
+ * Don't use this function to free each data. Use
+ * nghttp3_map_each_free() instead.
+ */
+int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr);
+
+void nghttp3_map_print_distance(nghttp3_map *map);
+
+#endif /* NGHTTP3_MAP_H */
diff --git a/lib/nghttp3_mem.c b/lib/nghttp3_mem.c
new file mode 100644
index 0000000..0379e99
--- /dev/null
+++ b/lib/nghttp3_mem.c
@@ -0,0 +1,124 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_mem.h"
+
+#include <stdio.h>
+
+static void *default_malloc(size_t size, void *user_data) {
+ (void)user_data;
+
+ return malloc(size);
+}
+
+static void default_free(void *ptr, void *user_data) {
+ (void)user_data;
+
+ free(ptr);
+}
+
+static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
+ (void)user_data;
+
+ return calloc(nmemb, size);
+}
+
+static void *default_realloc(void *ptr, size_t size, void *user_data) {
+ (void)user_data;
+
+ return realloc(ptr, size);
+}
+
+static nghttp3_mem mem_default = {NULL, default_malloc, default_free,
+ default_calloc, default_realloc};
+
+const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; }
+
+#ifndef MEMDEBUG
+void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) {
+ return mem->malloc(size, mem->user_data);
+}
+
+void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) {
+ mem->free(ptr, mem->user_data);
+}
+
+void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) {
+ return mem->calloc(nmemb, size, mem->user_data);
+}
+
+void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) {
+ return mem->realloc(ptr, size, mem->user_data);
+}
+#else /* MEMDEBUG */
+void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->malloc(size, mem->user_data);
+
+ fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
+ file, line);
+
+ return nptr;
+}
+
+void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ mem->free(ptr, mem->user_data);
+}
+
+void nghttp3_mem_free2_debug(const nghttp3_free free_func, void *ptr,
+ void *user_data, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ free_func(ptr, user_data);
+}
+
+void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
+ size_t size, const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->calloc(nmemb, size, mem->user_data);
+
+ fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
+ size, func, file, line);
+
+ return nptr;
+}
+
+void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->realloc(ptr, size, mem->user_data);
+
+ fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
+ size, func, file, line);
+
+ return nptr;
+}
+#endif /* MEMDEBUG */
diff --git a/lib/nghttp3_mem.h b/lib/nghttp3_mem.h
new file mode 100644
index 0000000..d6c3ada
--- /dev/null
+++ b/lib/nghttp3_mem.h
@@ -0,0 +1,80 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MEM_H
+#define NGHTTP3_MEM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/* Convenient wrapper functions to call allocator function in
+ |mem|. */
+#ifndef MEMDEBUG
+void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size);
+void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr);
+void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size);
+void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size);
+#else /* MEMDEBUG */
+void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define nghttp3_mem_malloc(MEM, SIZE) \
+ nghttp3_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
+
+void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line);
+
+# define nghttp3_mem_free(MEM, PTR) \
+ nghttp3_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
+
+void nghttp3_mem_free2_debug(nghttp3_free free_func, void *ptr, void *user_data,
+ const char *func, const char *file, size_t line);
+
+# define nghttp3_mem_free2(FREE_FUNC, PTR, USER_DATA) \
+ nghttp3_mem_free2_debug((FREE_FUNC), (PTR), (USER_DATA), __func__, \
+ __FILE__, __LINE__)
+
+void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
+ size_t size, const char *func, const char *file,
+ size_t line);
+
+# define nghttp3_mem_calloc(MEM, NMEMB, SIZE) \
+ nghttp3_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \
+ __LINE__)
+
+void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line);
+
+# define nghttp3_mem_realloc(MEM, PTR, SIZE) \
+ nghttp3_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, \
+ __LINE__)
+#endif /* MEMDEBUG */
+
+#endif /* NGHTTP3_MEM_H */
diff --git a/lib/nghttp3_objalloc.c b/lib/nghttp3_objalloc.c
new file mode 100644
index 0000000..0c97860
--- /dev/null
+++ b/lib/nghttp3_objalloc.c
@@ -0,0 +1,41 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_objalloc.h"
+
+void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
+ const nghttp3_mem *mem) {
+ nghttp3_balloc_init(&objalloc->balloc, blklen, mem);
+ nghttp3_opl_init(&objalloc->opl);
+}
+
+void nghttp3_objalloc_free(nghttp3_objalloc *objalloc) {
+ nghttp3_balloc_free(&objalloc->balloc);
+}
+
+void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc) {
+ nghttp3_opl_clear(&objalloc->opl);
+ nghttp3_balloc_clear(&objalloc->balloc);
+}
diff --git a/lib/nghttp3_objalloc.h b/lib/nghttp3_objalloc.h
new file mode 100644
index 0000000..da39447
--- /dev/null
+++ b/lib/nghttp3_objalloc.h
@@ -0,0 +1,141 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_OBJALLOC_H
+#define NGHTTP3_OBJALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_balloc.h"
+#include "nghttp3_opl.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_mem.h"
+
+/*
+ * nghttp3_objalloc combines nghttp3_balloc and nghttp3_opl, and
+ * provides an object pool with the custom allocator to reduce the
+ * allocation and deallocation overheads for small objects.
+ */
+typedef struct nghttp3_objalloc {
+ nghttp3_balloc balloc;
+ nghttp3_opl opl;
+} nghttp3_objalloc;
+
+/*
+ * nghttp3_objalloc_init initializes |objalloc|. |blklen| is directly
+ * passed to nghttp3_balloc_init.
+ */
+void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_objalloc_free releases all allocated resources.
+ */
+void nghttp3_objalloc_free(nghttp3_objalloc *objalloc);
+
+/*
+ * nghttp3_objalloc_clear releases all allocated resources and
+ * initializes its state.
+ */
+void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc);
+
+#ifndef NOMEMPOOL
+# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void nghttp3_objalloc_##NAME##_init( \
+ nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
+ nghttp3_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_get( \
+ nghttp3_objalloc *objalloc) { \
+ nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, \
+ sizeof(TYPE)); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
+ nghttp3_objalloc *objalloc, size_t len) { \
+ nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, len); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static void nghttp3_objalloc_##NAME##_release( \
+ nghttp3_objalloc *objalloc, TYPE *obj) { \
+ nghttp3_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \
+ }
+#else /* NOMEMPOOL */
+# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void nghttp3_objalloc_##NAME##_init( \
+ nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
+ nghttp3_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_get( \
+ nghttp3_objalloc *objalloc) { \
+ return nghttp3_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
+ nghttp3_objalloc *objalloc, size_t len) { \
+ return nghttp3_mem_malloc(objalloc->balloc.mem, len); \
+ } \
+ \
+ inline static void nghttp3_objalloc_##NAME##_release( \
+ nghttp3_objalloc *objalloc, TYPE *obj) { \
+ nghttp3_mem_free(objalloc->balloc.mem, obj); \
+ }
+#endif /* NOMEMPOOL */
+
+#endif /* NGHTTP3_OBJALLOC_H */
diff --git a/lib/nghttp3_opl.c b/lib/nghttp3_opl.c
new file mode 100644
index 0000000..eb8ebdd
--- /dev/null
+++ b/lib/nghttp3_opl.c
@@ -0,0 +1,47 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_opl.h"
+
+void nghttp3_opl_init(nghttp3_opl *opl) { opl->head = NULL; }
+
+void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent) {
+ ent->next = opl->head;
+ opl->head = ent;
+}
+
+nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl) {
+ nghttp3_opl_entry *ent = opl->head;
+
+ if (!ent) {
+ return NULL;
+ }
+
+ opl->head = ent->next;
+
+ return ent;
+}
+
+void nghttp3_opl_clear(nghttp3_opl *opl) { opl->head = NULL; }
diff --git a/lib/nghttp3_opl.h b/lib/nghttp3_opl.h
new file mode 100644
index 0000000..8c8a4f2
--- /dev/null
+++ b/lib/nghttp3_opl.h
@@ -0,0 +1,66 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_OPL_H
+#define NGHTTP3_OPL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_opl_entry nghttp3_opl_entry;
+
+struct nghttp3_opl_entry {
+ nghttp3_opl_entry *next;
+};
+
+/*
+ * nghttp3_opl is an object memory pool.
+ */
+typedef struct nghttp3_opl {
+ nghttp3_opl_entry *head;
+} nghttp3_opl;
+
+/*
+ * nghttp3_opl_init initializes |opl|.
+ */
+void nghttp3_opl_init(nghttp3_opl *opl);
+
+/*
+ * nghttp3_opl_push inserts |ent| to |opl| head.
+ */
+void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent);
+
+/*
+ * nghttp3_opl_pop removes the first nghttp3_opl_entry from |opl| and
+ * returns it. If |opl| does not have any entry, it returns NULL.
+ */
+nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl);
+
+void nghttp3_opl_clear(nghttp3_opl *opl);
+
+#endif /* NGHTTP3_OPL_H */
diff --git a/lib/nghttp3_pq.c b/lib/nghttp3_pq.c
new file mode 100644
index 0000000..5d09050
--- /dev/null
+++ b/lib/nghttp3_pq.c
@@ -0,0 +1,168 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_pq.h"
+
+#include <assert.h>
+
+#include "nghttp3_macro.h"
+
+void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less,
+ const nghttp3_mem *mem) {
+ pq->mem = mem;
+ pq->capacity = 0;
+ pq->q = NULL;
+ pq->length = 0;
+ pq->less = less;
+}
+
+void nghttp3_pq_free(nghttp3_pq *pq) {
+ nghttp3_mem_free(pq->mem, pq->q);
+ pq->q = NULL;
+}
+
+static void swap(nghttp3_pq *pq, size_t i, size_t j) {
+ nghttp3_pq_entry *a = pq->q[i];
+ nghttp3_pq_entry *b = pq->q[j];
+
+ pq->q[i] = b;
+ b->index = i;
+ pq->q[j] = a;
+ a->index = j;
+}
+
+static void bubble_up(nghttp3_pq *pq, size_t index) {
+ size_t parent;
+ while (index != 0) {
+ parent = (index - 1) / 2;
+ if (!pq->less(pq->q[index], pq->q[parent])) {
+ return;
+ }
+ swap(pq, parent, index);
+ index = parent;
+ }
+}
+
+int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item) {
+ if (pq->capacity <= pq->length) {
+ void *nq;
+ size_t ncapacity;
+
+ ncapacity = nghttp3_max(4, (pq->capacity * 2));
+
+ nq = nghttp3_mem_realloc(pq->mem, pq->q,
+ ncapacity * sizeof(nghttp3_pq_entry *));
+ if (nq == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+ pq->capacity = ncapacity;
+ pq->q = nq;
+ }
+ pq->q[pq->length] = item;
+ item->index = pq->length;
+ ++pq->length;
+ bubble_up(pq, pq->length - 1);
+ return 0;
+}
+
+nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq) {
+ assert(pq->length);
+ return pq->q[0];
+}
+
+static void bubble_down(nghttp3_pq *pq, size_t index) {
+ size_t i, j, minindex;
+ for (;;) {
+ j = index * 2 + 1;
+ minindex = index;
+ for (i = 0; i < 2; ++i, ++j) {
+ if (j >= pq->length) {
+ break;
+ }
+ if (pq->less(pq->q[j], pq->q[minindex])) {
+ minindex = j;
+ }
+ }
+ if (minindex == index) {
+ return;
+ }
+ swap(pq, index, minindex);
+ index = minindex;
+ }
+}
+
+void nghttp3_pq_pop(nghttp3_pq *pq) {
+ if (pq->length > 0) {
+ pq->q[0] = pq->q[pq->length - 1];
+ pq->q[0]->index = 0;
+ --pq->length;
+ bubble_down(pq, 0);
+ }
+}
+
+void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item) {
+ assert(pq->q[item->index] == item);
+
+ if (item->index == 0) {
+ nghttp3_pq_pop(pq);
+ return;
+ }
+
+ if (item->index == pq->length - 1) {
+ --pq->length;
+ return;
+ }
+
+ pq->q[item->index] = pq->q[pq->length - 1];
+ pq->q[item->index]->index = item->index;
+ --pq->length;
+
+ if (pq->less(item, pq->q[item->index])) {
+ bubble_down(pq, item->index);
+ } else {
+ bubble_up(pq, item->index);
+ }
+}
+
+int nghttp3_pq_empty(const nghttp3_pq *pq) { return pq->length == 0; }
+
+size_t nghttp3_pq_size(const nghttp3_pq *pq) { return pq->length; }
+
+int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg) {
+ size_t i;
+
+ if (pq->length == 0) {
+ return 0;
+ }
+ for (i = 0; i < pq->length; ++i) {
+ if ((*fun)(pq->q[i], arg)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void nghttp3_pq_clear(nghttp3_pq *pq) { pq->length = 0; }
diff --git a/lib/nghttp3_pq.h b/lib/nghttp3_pq.h
new file mode 100644
index 0000000..c1a54f5
--- /dev/null
+++ b/lib/nghttp3_pq.h
@@ -0,0 +1,129 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_PQ_H
+#define NGHTTP3_PQ_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+/* Implementation of priority queue */
+
+/* NGHTTP3_PQ_BAD_INDEX is the priority queue index which indicates
+ that an entry is not queued. Assigning this value to
+ nghttp3_pq_entry.index can check that the entry is queued or not. */
+#define NGHTTP3_PQ_BAD_INDEX SIZE_MAX
+
+typedef struct nghttp3_pq_entry {
+ size_t index;
+} nghttp3_pq_entry;
+
+/* "less" function, return nonzero if |lhs| is less than |rhs|. */
+typedef int (*nghttp3_less)(const nghttp3_pq_entry *lhs,
+ const nghttp3_pq_entry *rhs);
+
+typedef struct nghttp3_pq {
+ /* The pointer to the pointer to the item stored */
+ nghttp3_pq_entry **q;
+ /* Memory allocator */
+ const nghttp3_mem *mem;
+ /* The number of items stored */
+ size_t length;
+ /* The maximum number of items this pq can store. This is
+ automatically extended when length is reached to this value. */
+ size_t capacity;
+ /* The less function between items */
+ nghttp3_less less;
+} nghttp3_pq;
+
+/*
+ * Initializes priority queue |pq| with compare function |cmp|.
+ */
+void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, const nghttp3_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |pq|. The stored items are
+ * not freed by this function.
+ */
+void nghttp3_pq_free(nghttp3_pq *pq);
+
+/*
+ * Adds |item| to the priority queue |pq|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item);
+
+/*
+ * Returns item at the top of the queue |pq|. It is undefined if the
+ * queue is empty.
+ */
+nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq);
+
+/*
+ * Pops item at the top of the queue |pq|. The popped item is not
+ * freed by this function.
+ */
+void nghttp3_pq_pop(nghttp3_pq *pq);
+
+/*
+ * Returns nonzero if the queue |pq| is empty.
+ */
+int nghttp3_pq_empty(const nghttp3_pq *pq);
+
+/*
+ * Returns the number of items in the queue |pq|.
+ */
+size_t nghttp3_pq_size(const nghttp3_pq *pq);
+
+typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg);
+
+/*
+ * Applies |fun| to each item in |pq|. The |arg| is passed as arg
+ * parameter to callback function. This function must not change the
+ * ordering key. If the return value from callback is nonzero, this
+ * function returns 1 immediately without iterating remaining items.
+ * Otherwise this function returns 0.
+ */
+int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg);
+
+/*
+ * Removes |item| from priority queue.
+ */
+void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item);
+
+void nghttp3_pq_clear(nghttp3_pq *pq);
+
+#endif /* NGHTTP3_PQ_H */
diff --git a/lib/nghttp3_qpack.c b/lib/nghttp3_qpack.c
new file mode 100644
index 0000000..fece8f1
--- /dev/null
+++ b/lib/nghttp3_qpack.c
@@ -0,0 +1,4093 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_str.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_debug.h"
+#include "nghttp3_unreachable.h"
+
+/* NGHTTP3_QPACK_MAX_QPACK_STREAMS is the maximum number of concurrent
+ nghttp3_qpack_stream object to handle a client which never cancel
+ or acknowledge header block. After this limit, encoder stops using
+ dynamic table. */
+#define NGHTTP3_QPACK_MAX_QPACK_STREAMS 2000
+
+/* Make scalar initialization form of nghttp3_qpack_static_entry */
+#define MAKE_STATIC_ENT(I, T, H) \
+ { I, T, H }
+
+/* Generated by mkstatichdtbl.py */
+static nghttp3_qpack_static_entry token_stable[] = {
+ MAKE_STATIC_ENT(0, NGHTTP3_QPACK_TOKEN__AUTHORITY, 3153725150u),
+ MAKE_STATIC_ENT(15, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(16, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(17, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(18, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(19, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(20, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(21, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(1, NGHTTP3_QPACK_TOKEN__PATH, 3292848686u),
+ MAKE_STATIC_ENT(22, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u),
+ MAKE_STATIC_ENT(23, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u),
+ MAKE_STATIC_ENT(24, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(25, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(26, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(27, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(28, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(63, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(64, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(65, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(66, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(67, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(68, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(69, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(70, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(71, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(29, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u),
+ MAKE_STATIC_ENT(30, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u),
+ MAKE_STATIC_ENT(31, NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING, 3379649177u),
+ MAKE_STATIC_ENT(72, NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE, 1979086614u),
+ MAKE_STATIC_ENT(32, NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES, 1713753958u),
+ MAKE_STATIC_ENT(73, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ 901040780u),
+ MAKE_STATIC_ENT(74, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ 901040780u),
+ MAKE_STATIC_ENT(33, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(34, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(75, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(76, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(77, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(78, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(35, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN,
+ 2710797292u),
+ MAKE_STATIC_ENT(79, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS,
+ 2449824425u),
+ MAKE_STATIC_ENT(80, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS,
+ 3599549072u),
+ MAKE_STATIC_ENT(81, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD,
+ 2417078055u),
+ MAKE_STATIC_ENT(82, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD,
+ 2417078055u),
+ MAKE_STATIC_ENT(2, NGHTTP3_QPACK_TOKEN_AGE, 742476188u),
+ MAKE_STATIC_ENT(83, NGHTTP3_QPACK_TOKEN_ALT_SVC, 2148877059u),
+ MAKE_STATIC_ENT(84, NGHTTP3_QPACK_TOKEN_AUTHORIZATION, 2436257726u),
+ MAKE_STATIC_ENT(36, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(37, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(38, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(39, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(40, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(41, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(3, NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION, 3889184348u),
+ MAKE_STATIC_ENT(42, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u),
+ MAKE_STATIC_ENT(43, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u),
+ MAKE_STATIC_ENT(4, NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH, 1308181789u),
+ MAKE_STATIC_ENT(85, NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY,
+ 1569039836u),
+ MAKE_STATIC_ENT(44, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(45, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(46, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(47, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(48, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(49, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(50, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(51, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(52, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(53, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(54, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(5, NGHTTP3_QPACK_TOKEN_COOKIE, 2007449791u),
+ MAKE_STATIC_ENT(6, NGHTTP3_QPACK_TOKEN_DATE, 3564297305u),
+ MAKE_STATIC_ENT(86, NGHTTP3_QPACK_TOKEN_EARLY_DATA, 4080895051u),
+ MAKE_STATIC_ENT(7, NGHTTP3_QPACK_TOKEN_ETAG, 113792960u),
+ MAKE_STATIC_ENT(87, NGHTTP3_QPACK_TOKEN_EXPECT_CT, 1183214960u),
+ MAKE_STATIC_ENT(88, NGHTTP3_QPACK_TOKEN_FORWARDED, 1485178027u),
+ MAKE_STATIC_ENT(8, NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE, 2213050793u),
+ MAKE_STATIC_ENT(9, NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH, 2536202615u),
+ MAKE_STATIC_ENT(89, NGHTTP3_QPACK_TOKEN_IF_RANGE, 2340978238u),
+ MAKE_STATIC_ENT(10, NGHTTP3_QPACK_TOKEN_LAST_MODIFIED, 3226950251u),
+ MAKE_STATIC_ENT(11, NGHTTP3_QPACK_TOKEN_LINK, 232457833u),
+ MAKE_STATIC_ENT(12, NGHTTP3_QPACK_TOKEN_LOCATION, 200649126u),
+ MAKE_STATIC_ENT(90, NGHTTP3_QPACK_TOKEN_ORIGIN, 3649018447u),
+ MAKE_STATIC_ENT(91, NGHTTP3_QPACK_TOKEN_PURPOSE, 4212263681u),
+ MAKE_STATIC_ENT(55, NGHTTP3_QPACK_TOKEN_RANGE, 4208725202u),
+ MAKE_STATIC_ENT(13, NGHTTP3_QPACK_TOKEN_REFERER, 3969579366u),
+ MAKE_STATIC_ENT(92, NGHTTP3_QPACK_TOKEN_SERVER, 1085029842u),
+ MAKE_STATIC_ENT(14, NGHTTP3_QPACK_TOKEN_SET_COOKIE, 1848371000u),
+ MAKE_STATIC_ENT(56, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(57, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(58, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(93, NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN, 2432297564u),
+ MAKE_STATIC_ENT(94, NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS,
+ 2479169413u),
+ MAKE_STATIC_ENT(95, NGHTTP3_QPACK_TOKEN_USER_AGENT, 606444526u),
+ MAKE_STATIC_ENT(59, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u),
+ MAKE_STATIC_ENT(60, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u),
+ MAKE_STATIC_ENT(61, NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS,
+ 3644557769u),
+ MAKE_STATIC_ENT(96, NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR, 2914187656u),
+ MAKE_STATIC_ENT(97, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u),
+ MAKE_STATIC_ENT(98, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u),
+ MAKE_STATIC_ENT(62, NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION, 2501058888u),
+};
+
+/* Make scalar initialization form of nghttp3_qpack_static_entry */
+#define MAKE_STATIC_HD(N, V, T) \
+ { \
+ {NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
+ {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \
+ }
+
+static nghttp3_qpack_static_header stable[] = {
+ MAKE_STATIC_HD(":authority", "", NGHTTP3_QPACK_TOKEN__AUTHORITY),
+ MAKE_STATIC_HD(":path", "/", NGHTTP3_QPACK_TOKEN__PATH),
+ MAKE_STATIC_HD("age", "0", NGHTTP3_QPACK_TOKEN_AGE),
+ MAKE_STATIC_HD("content-disposition", "",
+ NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION),
+ MAKE_STATIC_HD("content-length", "0", NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH),
+ MAKE_STATIC_HD("cookie", "", NGHTTP3_QPACK_TOKEN_COOKIE),
+ MAKE_STATIC_HD("date", "", NGHTTP3_QPACK_TOKEN_DATE),
+ MAKE_STATIC_HD("etag", "", NGHTTP3_QPACK_TOKEN_ETAG),
+ MAKE_STATIC_HD("if-modified-since", "",
+ NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE),
+ MAKE_STATIC_HD("if-none-match", "", NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH),
+ MAKE_STATIC_HD("last-modified", "", NGHTTP3_QPACK_TOKEN_LAST_MODIFIED),
+ MAKE_STATIC_HD("link", "", NGHTTP3_QPACK_TOKEN_LINK),
+ MAKE_STATIC_HD("location", "", NGHTTP3_QPACK_TOKEN_LOCATION),
+ MAKE_STATIC_HD("referer", "", NGHTTP3_QPACK_TOKEN_REFERER),
+ MAKE_STATIC_HD("set-cookie", "", NGHTTP3_QPACK_TOKEN_SET_COOKIE),
+ MAKE_STATIC_HD(":method", "CONNECT", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "DELETE", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "GET", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "HEAD", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "OPTIONS", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "POST", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "PUT", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":scheme", "http", NGHTTP3_QPACK_TOKEN__SCHEME),
+ MAKE_STATIC_HD(":scheme", "https", NGHTTP3_QPACK_TOKEN__SCHEME),
+ MAKE_STATIC_HD(":status", "103", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "200", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "304", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "404", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "503", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD("accept", "*/*", NGHTTP3_QPACK_TOKEN_ACCEPT),
+ MAKE_STATIC_HD("accept", "application/dns-message",
+ NGHTTP3_QPACK_TOKEN_ACCEPT),
+ MAKE_STATIC_HD("accept-encoding", "gzip, deflate, br",
+ NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING),
+ MAKE_STATIC_HD("accept-ranges", "bytes", NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES),
+ MAKE_STATIC_HD("access-control-allow-headers", "cache-control",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-headers", "content-type",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-origin", "*",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN),
+ MAKE_STATIC_HD("cache-control", "max-age=0",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "max-age=2592000",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "max-age=604800",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "no-cache",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "no-store",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "public, max-age=31536000",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("content-encoding", "br",
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING),
+ MAKE_STATIC_HD("content-encoding", "gzip",
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING),
+ MAKE_STATIC_HD("content-type", "application/dns-message",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/javascript",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/json",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/x-www-form-urlencoded",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/gif",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/jpeg",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/png",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/css",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/html; charset=utf-8",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/plain",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/plain;charset=utf-8",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("range", "bytes=0-", NGHTTP3_QPACK_TOKEN_RANGE),
+ MAKE_STATIC_HD("strict-transport-security", "max-age=31536000",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("strict-transport-security",
+ "max-age=31536000; includesubdomains",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("strict-transport-security",
+ "max-age=31536000; includesubdomains; preload",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("vary", "accept-encoding", NGHTTP3_QPACK_TOKEN_VARY),
+ MAKE_STATIC_HD("vary", "origin", NGHTTP3_QPACK_TOKEN_VARY),
+ MAKE_STATIC_HD("x-content-type-options", "nosniff",
+ NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS),
+ MAKE_STATIC_HD("x-xss-protection", "1; mode=block",
+ NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION),
+ MAKE_STATIC_HD(":status", "100", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "204", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "206", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "302", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "400", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "403", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "421", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "425", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "500", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD("accept-language", "", NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE),
+ MAKE_STATIC_HD("access-control-allow-credentials", "FALSE",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS),
+ MAKE_STATIC_HD("access-control-allow-credentials", "TRUE",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS),
+ MAKE_STATIC_HD("access-control-allow-headers", "*",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-methods", "get",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-allow-methods", "get, post, options",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-allow-methods", "options",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-expose-headers", "content-length",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS),
+ MAKE_STATIC_HD("access-control-request-headers", "content-type",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS),
+ MAKE_STATIC_HD("access-control-request-method", "get",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD),
+ MAKE_STATIC_HD("access-control-request-method", "post",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD),
+ MAKE_STATIC_HD("alt-svc", "clear", NGHTTP3_QPACK_TOKEN_ALT_SVC),
+ MAKE_STATIC_HD("authorization", "", NGHTTP3_QPACK_TOKEN_AUTHORIZATION),
+ MAKE_STATIC_HD("content-security-policy",
+ "script-src 'none'; object-src 'none'; base-uri 'none'",
+ NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY),
+ MAKE_STATIC_HD("early-data", "1", NGHTTP3_QPACK_TOKEN_EARLY_DATA),
+ MAKE_STATIC_HD("expect-ct", "", NGHTTP3_QPACK_TOKEN_EXPECT_CT),
+ MAKE_STATIC_HD("forwarded", "", NGHTTP3_QPACK_TOKEN_FORWARDED),
+ MAKE_STATIC_HD("if-range", "", NGHTTP3_QPACK_TOKEN_IF_RANGE),
+ MAKE_STATIC_HD("origin", "", NGHTTP3_QPACK_TOKEN_ORIGIN),
+ MAKE_STATIC_HD("purpose", "prefetch", NGHTTP3_QPACK_TOKEN_PURPOSE),
+ MAKE_STATIC_HD("server", "", NGHTTP3_QPACK_TOKEN_SERVER),
+ MAKE_STATIC_HD("timing-allow-origin", "*",
+ NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN),
+ MAKE_STATIC_HD("upgrade-insecure-requests", "1",
+ NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS),
+ MAKE_STATIC_HD("user-agent", "", NGHTTP3_QPACK_TOKEN_USER_AGENT),
+ MAKE_STATIC_HD("x-forwarded-for", "", NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR),
+ MAKE_STATIC_HD("x-frame-options", "deny",
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS),
+ MAKE_STATIC_HD("x-frame-options", "sameorigin",
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS),
+};
+
+static int memeq(const void *s1, const void *s2, size_t n) {
+ return n == 0 || memcmp(s1, s2, n) == 0;
+}
+
+/* Generated by genlibtokenlookup.py */
+static int32_t qpack_lookup_token(const uint8_t *name, size_t namelen) {
+ switch (namelen) {
+ case 2:
+ switch (name[1]) {
+ case 'e':
+ if (memeq("t", name, 1)) {
+ return NGHTTP3_QPACK_TOKEN_TE;
+ }
+ break;
+ }
+ break;
+ case 3:
+ switch (name[2]) {
+ case 'e':
+ if (memeq("ag", name, 2)) {
+ return NGHTTP3_QPACK_TOKEN_AGE;
+ }
+ break;
+ }
+ break;
+ case 4:
+ switch (name[3]) {
+ case 'e':
+ if (memeq("dat", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_DATE;
+ }
+ break;
+ case 'g':
+ if (memeq("eta", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_ETAG;
+ }
+ break;
+ case 'k':
+ if (memeq("lin", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_LINK;
+ }
+ break;
+ case 't':
+ if (memeq("hos", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_HOST;
+ }
+ break;
+ case 'y':
+ if (memeq("var", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_VARY;
+ }
+ break;
+ }
+ break;
+ case 5:
+ switch (name[4]) {
+ case 'e':
+ if (memeq("rang", name, 4)) {
+ return NGHTTP3_QPACK_TOKEN_RANGE;
+ }
+ break;
+ case 'h':
+ if (memeq(":pat", name, 4)) {
+ return NGHTTP3_QPACK_TOKEN__PATH;
+ }
+ break;
+ }
+ break;
+ case 6:
+ switch (name[5]) {
+ case 'e':
+ if (memeq("cooki", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_COOKIE;
+ }
+ break;
+ case 'n':
+ if (memeq("origi", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_ORIGIN;
+ }
+ break;
+ case 'r':
+ if (memeq("serve", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_SERVER;
+ }
+ break;
+ case 't':
+ if (memeq("accep", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT;
+ }
+ break;
+ }
+ break;
+ case 7:
+ switch (name[6]) {
+ case 'c':
+ if (memeq("alt-sv", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_ALT_SVC;
+ }
+ break;
+ case 'd':
+ if (memeq(":metho", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__METHOD;
+ }
+ break;
+ case 'e':
+ if (memeq(":schem", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__SCHEME;
+ }
+ if (memeq("purpos", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_PURPOSE;
+ }
+ if (memeq("upgrad", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_UPGRADE;
+ }
+ break;
+ case 'r':
+ if (memeq("refere", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_REFERER;
+ }
+ break;
+ case 's':
+ if (memeq(":statu", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__STATUS;
+ }
+ break;
+ }
+ break;
+ case 8:
+ switch (name[7]) {
+ case 'e':
+ if (memeq("if-rang", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_IF_RANGE;
+ }
+ break;
+ case 'n':
+ if (memeq("locatio", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_LOCATION;
+ }
+ break;
+ case 'y':
+ if (memeq("priorit", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_PRIORITY;
+ }
+ break;
+ }
+ break;
+ case 9:
+ switch (name[8]) {
+ case 'd':
+ if (memeq("forwarde", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN_FORWARDED;
+ }
+ break;
+ case 'l':
+ if (memeq(":protoco", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN__PROTOCOL;
+ }
+ break;
+ case 't':
+ if (memeq("expect-c", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN_EXPECT_CT;
+ }
+ break;
+ }
+ break;
+ case 10:
+ switch (name[9]) {
+ case 'a':
+ if (memeq("early-dat", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_EARLY_DATA;
+ }
+ break;
+ case 'e':
+ if (memeq("keep-aliv", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_KEEP_ALIVE;
+ }
+ if (memeq("set-cooki", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_SET_COOKIE;
+ }
+ break;
+ case 'n':
+ if (memeq("connectio", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_CONNECTION;
+ }
+ break;
+ case 't':
+ if (memeq("user-agen", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_USER_AGENT;
+ }
+ break;
+ case 'y':
+ if (memeq(":authorit", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN__AUTHORITY;
+ }
+ break;
+ }
+ break;
+ case 12:
+ switch (name[11]) {
+ case 'e':
+ if (memeq("content-typ", name, 11)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_TYPE;
+ }
+ break;
+ }
+ break;
+ case 13:
+ switch (name[12]) {
+ case 'd':
+ if (memeq("last-modifie", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_LAST_MODIFIED;
+ }
+ break;
+ case 'h':
+ if (memeq("if-none-matc", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH;
+ }
+ break;
+ case 'l':
+ if (memeq("cache-contro", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_CACHE_CONTROL;
+ }
+ break;
+ case 'n':
+ if (memeq("authorizatio", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_AUTHORIZATION;
+ }
+ break;
+ case 's':
+ if (memeq("accept-range", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES;
+ }
+ break;
+ }
+ break;
+ case 14:
+ switch (name[13]) {
+ case 'h':
+ if (memeq("content-lengt", name, 13)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH;
+ }
+ break;
+ }
+ break;
+ case 15:
+ switch (name[14]) {
+ case 'e':
+ if (memeq("accept-languag", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE;
+ }
+ break;
+ case 'g':
+ if (memeq("accept-encodin", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING;
+ }
+ break;
+ case 'r':
+ if (memeq("x-forwarded-fo", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR;
+ }
+ break;
+ case 's':
+ if (memeq("x-frame-option", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS;
+ }
+ break;
+ }
+ break;
+ case 16:
+ switch (name[15]) {
+ case 'g':
+ if (memeq("content-encodin", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING;
+ }
+ break;
+ case 'n':
+ if (memeq("proxy-connectio", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION;
+ }
+ if (memeq("x-xss-protectio", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION;
+ }
+ break;
+ }
+ break;
+ case 17:
+ switch (name[16]) {
+ case 'e':
+ if (memeq("if-modified-sinc", name, 16)) {
+ return NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE;
+ }
+ break;
+ case 'g':
+ if (memeq("transfer-encodin", name, 16)) {
+ return NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING;
+ }
+ break;
+ }
+ break;
+ case 19:
+ switch (name[18]) {
+ case 'n':
+ if (memeq("content-dispositio", name, 18)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION;
+ }
+ if (memeq("timing-allow-origi", name, 18)) {
+ return NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN;
+ }
+ break;
+ }
+ break;
+ case 22:
+ switch (name[21]) {
+ case 's':
+ if (memeq("x-content-type-option", name, 21)) {
+ return NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS;
+ }
+ break;
+ }
+ break;
+ case 23:
+ switch (name[22]) {
+ case 'y':
+ if (memeq("content-security-polic", name, 22)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY;
+ }
+ break;
+ }
+ break;
+ case 25:
+ switch (name[24]) {
+ case 's':
+ if (memeq("upgrade-insecure-request", name, 24)) {
+ return NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS;
+ }
+ break;
+ case 'y':
+ if (memeq("strict-transport-securit", name, 24)) {
+ return NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY;
+ }
+ break;
+ }
+ break;
+ case 27:
+ switch (name[26]) {
+ case 'n':
+ if (memeq("access-control-allow-origi", name, 26)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN;
+ }
+ break;
+ }
+ break;
+ case 28:
+ switch (name[27]) {
+ case 's':
+ if (memeq("access-control-allow-header", name, 27)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS;
+ }
+ if (memeq("access-control-allow-method", name, 27)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS;
+ }
+ break;
+ }
+ break;
+ case 29:
+ switch (name[28]) {
+ case 'd':
+ if (memeq("access-control-request-metho", name, 28)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD;
+ }
+ break;
+ case 's':
+ if (memeq("access-control-expose-header", name, 28)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS;
+ }
+ break;
+ }
+ break;
+ case 30:
+ switch (name[29]) {
+ case 's':
+ if (memeq("access-control-request-header", name, 29)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS;
+ }
+ break;
+ }
+ break;
+ case 32:
+ switch (name[31]) {
+ case 's':
+ if (memeq("access-control-allow-credential", name, 31)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS;
+ }
+ break;
+ }
+ break;
+ }
+ return -1;
+}
+
+static size_t table_space(size_t namelen, size_t valuelen) {
+ return NGHTTP3_QPACK_ENTRY_OVERHEAD + namelen + valuelen;
+}
+
+static int qpack_nv_name_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) {
+ return a->name->len == b->namelen &&
+ memeq(a->name->base, b->name, b->namelen);
+}
+
+static int qpack_nv_value_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) {
+ return a->value->len == b->valuelen &&
+ memeq(a->value->base, b->value, b->valuelen);
+}
+
+static void qpack_map_init(nghttp3_qpack_map *map) {
+ memset(map, 0, sizeof(nghttp3_qpack_map));
+}
+
+static void qpack_map_insert(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
+ nghttp3_qpack_entry **bucket;
+
+ bucket = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)];
+
+ if (*bucket == NULL) {
+ *bucket = ent;
+ return;
+ }
+
+ /* larger absidx is linked near the root */
+ ent->map_next = *bucket;
+ *bucket = ent;
+}
+
+static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
+ nghttp3_qpack_entry **dst;
+
+ dst = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)];
+
+ for (; *dst; dst = &(*dst)->map_next) {
+ if (*dst != ent) {
+ continue;
+ }
+
+ *dst = ent->map_next;
+ ent->map_next = NULL;
+ return;
+ }
+}
+
+/*
+ * qpack_context_can_reference returns nonzero if dynamic table entry
+ * at |absidx| can be referenced. In other words, it is within
+ * ctx->max_dtable_capacity.
+ */
+static int qpack_context_can_reference(nghttp3_qpack_context *ctx,
+ uint64_t absidx) {
+ nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
+ return ctx->dtable_sum - ent->sum <= ctx->max_dtable_capacity;
+}
+
+/* |*ppb_match| (post-base match), if it is not NULL, is always exact
+ match. */
+static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
+ int *exact_match,
+ nghttp3_qpack_entry **pmatch,
+ nghttp3_qpack_entry **ppb_match,
+ const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, uint64_t krcnt,
+ int allow_blocking, int name_only) {
+ nghttp3_qpack_entry *p;
+
+ *exact_match = 0;
+ *pmatch = NULL;
+ *ppb_match = NULL;
+
+ for (p = encoder->dtable_map.table[hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; p;
+ p = p->map_next) {
+ if (token != p->nv.token ||
+ (token == -1 && (hash != p->hash || !qpack_nv_name_eq(&p->nv, nv))) ||
+ !qpack_context_can_reference(&encoder->ctx, p->absidx)) {
+ continue;
+ }
+ if (allow_blocking || p->absidx + 1 <= krcnt) {
+ if (!*pmatch) {
+ *pmatch = p;
+ if (name_only) {
+ return;
+ }
+ }
+ if (qpack_nv_value_eq(&p->nv, nv)) {
+ *pmatch = p;
+ *exact_match = 1;
+ return;
+ }
+ } else if (!*ppb_match && qpack_nv_value_eq(&p->nv, nv)) {
+ *ppb_match = p;
+ }
+ }
+}
+
+/*
+ * qpack_context_init initializes |ctx|. |hard_max_dtable_capacity|
+ * is the upper bound of the dynamic table capacity. |mem| is a
+ * memory allocator.
+ *
+ * The maximum dynamic table size is governed by
+ * ctx->max_dtable_capacity and it is initialized to 0.
+ * |hard_max_dtable_capacity| is the upper bound of
+ * ctx->max_dtable_capacity.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_context_init(nghttp3_qpack_context *ctx,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem) {
+ int rv;
+ size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ size_t len2;
+
+ for (len2 = 1; len2 < len; len2 <<= 1)
+ ;
+
+ rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *),
+ mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ctx->mem = mem;
+ ctx->dtable_size = 0;
+ ctx->dtable_sum = 0;
+ ctx->hard_max_dtable_capacity = hard_max_dtable_capacity;
+ ctx->max_dtable_capacity = 0;
+ ctx->max_blocked_streams = max_blocked_streams;
+ ctx->next_absidx = 0;
+ ctx->bad = 0;
+
+ return 0;
+}
+
+static void qpack_context_free(nghttp3_qpack_context *ctx) {
+ nghttp3_qpack_entry *ent;
+ size_t i, len = nghttp3_ringbuf_len(&ctx->dtable);
+
+ for (i = 0; i < len; ++i) {
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i);
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(ctx->mem, ent);
+ }
+ nghttp3_ringbuf_free(&ctx->dtable);
+}
+
+static int ref_min_cnt_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ nghttp3_qpack_header_block_ref *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, min_cnts_pe);
+ nghttp3_qpack_header_block_ref *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, min_cnts_pe);
+
+ return lhs->min_cnt < rhs->min_cnt;
+}
+
+typedef struct nghttp3_blocked_streams_key {
+ uint64_t max_cnt;
+ uint64_t id;
+} nghttp3_blocked_streams_key;
+
+static int max_cnt_greater(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_blocked_streams_key *a = lhs;
+ const nghttp3_blocked_streams_key *b = rhs;
+ return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id);
+}
+
+int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem) {
+ int rv;
+
+ rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nghttp3_map_init(&encoder->streams, mem);
+
+ nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
+ sizeof(nghttp3_blocked_streams_key), mem);
+
+ qpack_map_init(&encoder->dtable_map);
+ nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem);
+
+ encoder->krcnt = 0;
+ encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE;
+ encoder->opcode = 0;
+ encoder->min_dtable_update = SIZE_MAX;
+ encoder->last_max_dtable_update = 0;
+ encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE;
+
+ nghttp3_qpack_read_state_reset(&encoder->rstate);
+
+ return 0;
+}
+
+static int map_stream_free(void *data, void *ptr) {
+ const nghttp3_mem *mem = ptr;
+ nghttp3_qpack_stream *stream = data;
+ nghttp3_qpack_stream_del(stream, mem);
+ return 0;
+}
+
+void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) {
+ nghttp3_pq_free(&encoder->min_cnts);
+ nghttp3_ksl_free(&encoder->blocked_streams);
+ nghttp3_map_each_free(&encoder->streams, map_stream_free,
+ (void *)encoder->ctx.mem);
+ nghttp3_map_free(&encoder->streams);
+ qpack_context_free(&encoder->ctx);
+}
+
+void nghttp3_qpack_encoder_set_max_dtable_capacity(
+ nghttp3_qpack_encoder *encoder, size_t max_dtable_capacity) {
+ max_dtable_capacity =
+ nghttp3_min(max_dtable_capacity, encoder->ctx.hard_max_dtable_capacity);
+
+ if (encoder->ctx.max_dtable_capacity == max_dtable_capacity) {
+ return;
+ }
+
+ encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
+
+ if (encoder->min_dtable_update > max_dtable_capacity) {
+ encoder->min_dtable_update = max_dtable_capacity;
+ encoder->ctx.max_dtable_capacity = max_dtable_capacity;
+ }
+ encoder->last_max_dtable_update = max_dtable_capacity;
+}
+
+void nghttp3_qpack_encoder_set_max_blocked_streams(
+ nghttp3_qpack_encoder *encoder, size_t max_blocked_streams) {
+ encoder->ctx.max_blocked_streams = max_blocked_streams;
+}
+
+uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) {
+ assert(!nghttp3_pq_empty(&encoder->min_cnts));
+
+ return nghttp3_struct_of(nghttp3_pq_top(&encoder->min_cnts),
+ nghttp3_qpack_header_block_ref, min_cnts_pe)
+ ->min_cnt;
+}
+
+void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) {
+ nghttp3_ringbuf *dtable = &encoder->ctx.dtable;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ uint64_t min_cnt = UINT64_MAX;
+ size_t len;
+ nghttp3_qpack_entry *ent;
+
+ if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_capacity) {
+ return;
+ }
+
+ if (!nghttp3_pq_empty(&encoder->min_cnts)) {
+ min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
+ }
+
+ for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_capacity;) {
+ len = nghttp3_ringbuf_len(dtable);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
+ if (ent->absidx + 1 == min_cnt) {
+ return;
+ }
+
+ encoder->ctx.dtable_size -=
+ table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(dtable);
+ qpack_map_remove(&encoder->dtable_map, ent);
+
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+}
+
+/*
+ * qpack_encoder_add_stream_ref adds another dynamic table reference
+ * to a stream denoted by |stream_id|. |stream| must be NULL if no
+ * stream object is not found for the given stream ID. |max_cnt| and
+ * |min_cnt| is the maximum and minimum insert count it references
+ * respectively.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id,
+ nghttp3_qpack_stream *stream,
+ uint64_t max_cnt, uint64_t min_cnt) {
+ nghttp3_qpack_header_block_ref *ref;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ uint64_t prev_max_cnt = 0;
+ int rv;
+
+ if (stream == NULL) {
+ rv = nghttp3_qpack_stream_new(&stream, stream_id, mem);
+ if (rv != 0) {
+ assert(rv == NGHTTP3_ERR_NOMEM);
+ return rv;
+ }
+ rv = nghttp3_map_insert(&encoder->streams,
+ (nghttp3_map_key_type)stream->stream_id, stream);
+ if (rv != 0) {
+ assert(rv == NGHTTP3_ERR_NOMEM);
+ nghttp3_qpack_stream_del(stream, mem);
+ return rv;
+ }
+ } else {
+ prev_max_cnt = nghttp3_qpack_stream_get_max_cnt(stream);
+ if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream) &&
+ max_cnt > prev_max_cnt) {
+ nghttp3_qpack_encoder_unblock_stream(encoder, stream);
+ }
+ }
+
+ rv = nghttp3_qpack_header_block_ref_new(&ref, max_cnt, min_cnt, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_qpack_stream_add_ref(stream, ref);
+ if (rv != 0) {
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+ return rv;
+ }
+
+ if (max_cnt > prev_max_cnt &&
+ nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) {
+ rv = nghttp3_qpack_encoder_block_stream(encoder, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nghttp3_pq_push(&encoder->min_cnts, &ref->min_cnts_pe);
+}
+
+static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ size_t i, len;
+ nghttp3_qpack_header_block_ref *ref;
+
+ nghttp3_map_remove(&encoder->streams,
+ (nghttp3_map_key_type)stream->stream_id);
+
+ len = nghttp3_ringbuf_len(&stream->refs);
+ for (i = 0; i < len; ++i) {
+ ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs,
+ i);
+
+ assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe);
+ }
+}
+
+/*
+ * reserve_buf_internal ensures that |buf| contains at least
+ * |extra_size| of free space. In other words, if this function
+ * succeeds, nghttp2_buf_left(buf) >= extra_size holds. |min_size| is
+ * the minimum size of buffer. The allocated buffer has at least
+ * |min_size| bytes.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int reserve_buf_internal(nghttp3_buf *buf, size_t extra_size,
+ size_t min_size, const nghttp3_mem *mem) {
+ size_t left = nghttp3_buf_left(buf);
+ size_t n = min_size, need;
+
+ if (left >= extra_size) {
+ return 0;
+ }
+
+ need = nghttp3_buf_cap(buf) + extra_size - left;
+
+ for (; n < need; n *= 2)
+ ;
+
+ return nghttp3_buf_reserve(buf, n, mem);
+}
+
+static int reserve_buf_small(nghttp3_buf *buf, size_t extra_size,
+ const nghttp3_mem *mem) {
+ return reserve_buf_internal(buf, extra_size, 32, mem);
+}
+
+static int reserve_buf(nghttp3_buf *buf, size_t extra_size,
+ const nghttp3_mem *mem) {
+ return reserve_buf_internal(buf, extra_size, 32, mem);
+}
+
+int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *pbuf, nghttp3_buf *rbuf,
+ nghttp3_buf *ebuf, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ size_t i;
+ uint64_t max_cnt = 0, min_cnt = UINT64_MAX;
+ uint64_t base;
+ int rv = 0;
+ int allow_blocking;
+ int blocked_stream;
+ nghttp3_qpack_stream *stream;
+
+ if (encoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = nghttp3_qpack_encoder_process_dtable_update(encoder, ebuf);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ base = encoder->ctx.next_absidx;
+
+ stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id);
+ blocked_stream =
+ stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream);
+ allow_blocking =
+ blocked_stream || encoder->ctx.max_blocked_streams >
+ nghttp3_ksl_len(&encoder->blocked_streams);
+
+ DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id,
+ blocked_stream, allow_blocking);
+
+ for (i = 0; i < nvlen; ++i) {
+ rv = nghttp3_qpack_encoder_encode_nv(encoder, &max_cnt, &min_cnt, rbuf,
+ ebuf, &nva[i], base, allow_blocking);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ nghttp3_qpack_encoder_write_field_section_prefix(encoder, pbuf, max_cnt,
+ base);
+
+ /* TODO If max_cnt == 0, no reference is made to dtable. */
+ if (!max_cnt) {
+ return 0;
+ }
+
+ rv = qpack_encoder_add_stream_ref(encoder, stream_id, stream, max_cnt,
+ min_cnt);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ encoder->ctx.bad = 1;
+ return rv;
+}
+
+/*
+ * qpack_write_number writes variable integer to |rbuf|. |num| is an
+ * integer to write. |prefix| is a prefix of variable integer
+ * encoding.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_write_number(nghttp3_buf *rbuf, uint8_t fb, uint64_t num,
+ size_t prefix, const nghttp3_mem *mem) {
+ int rv;
+ size_t len = nghttp3_qpack_put_varint_len(num, prefix);
+ uint8_t *p;
+
+ rv = reserve_buf(rbuf, len, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = rbuf->last;
+
+ *p = fb;
+ p = nghttp3_qpack_put_varint(p, num, prefix);
+
+ assert((size_t)(p - rbuf->last) == len);
+
+ rbuf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf) {
+ int rv;
+
+ nghttp3_qpack_encoder_shrink_dtable(encoder);
+
+ if (encoder->ctx.max_dtable_capacity < encoder->ctx.dtable_size ||
+ !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) {
+ return 0;
+ }
+
+ if (encoder->min_dtable_update < encoder->last_max_dtable_update) {
+ rv = nghttp3_qpack_encoder_write_set_dtable_cap(encoder, ebuf,
+ encoder->min_dtable_update);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = nghttp3_qpack_encoder_write_set_dtable_cap(
+ encoder, ebuf, encoder->last_max_dtable_update);
+ if (rv != 0) {
+ return rv;
+ }
+
+ encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
+ encoder->min_dtable_update = SIZE_MAX;
+ encoder->ctx.max_dtable_capacity = encoder->last_max_dtable_update;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf, size_t cap) {
+ DEBUGF("qpack::encode: Set Dynamic Table Capacity capacity=%zu\n", cap);
+ return qpack_write_number(ebuf, 0x20, cap, 5, encoder->ctx.mem);
+}
+
+nghttp3_qpack_stream *
+nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id) {
+ return nghttp3_map_find(&encoder->streams, (nghttp3_map_key_type)stream_id);
+}
+
+int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ return stream && encoder->krcnt < nghttp3_qpack_stream_get_max_cnt(stream);
+}
+
+/*
+ * qpack_encoder_decide_indexing_mode determines and returns indexing
+ * mode for header field |nv|. |token| is a token of header field
+ * name.
+ */
+static nghttp3_qpack_indexing_mode
+qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv, int32_t token) {
+ if (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) {
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ }
+
+ switch (token) {
+ case NGHTTP3_QPACK_TOKEN_AUTHORIZATION:
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ case NGHTTP3_QPACK_TOKEN_COOKIE:
+ if (nv->valuelen < 20) {
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ }
+ break;
+ case -1:
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ case NGHTTP3_QPACK_TOKEN_AGE:
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH:
+ case NGHTTP3_QPACK_TOKEN_ETAG:
+ case NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE:
+ case NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH:
+ case NGHTTP3_QPACK_TOKEN_LOCATION:
+ case NGHTTP3_QPACK_TOKEN_SET_COOKIE:
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ case NGHTTP3_QPACK_TOKEN_TE:
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ break;
+ default:
+ if (token >= 1000) {
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ }
+ }
+
+ if (table_space(nv->namelen, nv->valuelen) >
+ encoder->ctx.max_dtable_capacity * 3 / 4) {
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ }
+
+ return NGHTTP3_QPACK_INDEXING_MODE_STORE;
+}
+
+/*
+ * qpack_encoder_can_index returns nonzero if an entry which occupies
+ * |need| bytes can be inserted into dynamic table. |min_cnt| is the
+ * minimum insert count which blocked stream requires.
+ */
+static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need,
+ uint64_t min_cnt) {
+ size_t avail = 0;
+ size_t len;
+ uint64_t gmin_cnt;
+ nghttp3_qpack_entry *min_ent, *last_ent;
+ nghttp3_ringbuf *dtable = &encoder->ctx.dtable;
+
+ if (encoder->ctx.max_dtable_capacity > encoder->ctx.dtable_size) {
+ avail = encoder->ctx.max_dtable_capacity - encoder->ctx.dtable_size;
+ if (need <= avail) {
+ return 1;
+ }
+ }
+
+ if (!nghttp3_pq_empty(&encoder->min_cnts)) {
+ gmin_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
+ min_cnt = nghttp3_min(min_cnt, gmin_cnt);
+ }
+
+ if (min_cnt == UINT64_MAX) {
+ return encoder->ctx.max_dtable_capacity >= need;
+ }
+
+ min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1);
+
+ len = nghttp3_ringbuf_len(&encoder->ctx.dtable);
+ assert(len);
+ last_ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
+
+ if (min_ent == last_ent) {
+ return 0;
+ }
+
+ return avail + min_ent->sum - last_ent->sum >= need;
+}
+
+/*
+ * qpack_encoder_can_index_nv returns nonzero if header field |nv| can
+ * be inserted into dynamic table. |min_cnt| is the minimum insert
+ * count which blocked stream requires.
+ */
+static int qpack_encoder_can_index_nv(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv, uint64_t min_cnt) {
+ return qpack_encoder_can_index(
+ encoder, table_space(nv->namelen, nv->valuelen), min_cnt);
+}
+
+/*
+ * qpack_encoder_can_index_duplicate returns nonzero if an entry at
+ * |absidx| in dynamic table can be inserted to dynamic table as
+ * duplicate. |min_cnt| is the minimum insert count which blocked
+ * stream requires.
+ */
+static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ uint64_t min_cnt) {
+ nghttp3_qpack_entry *ent =
+ nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ return qpack_encoder_can_index(
+ encoder, table_space(ent->nv.name->len, ent->nv.value->len), min_cnt);
+}
+
+/*
+ * qpack_context_check_draining returns nonzero if an entry at
+ * |absidx| in dynamic table is one of draining entries.
+ */
+static int qpack_context_check_draining(nghttp3_qpack_context *ctx,
+ uint64_t absidx) {
+ const size_t safe = ctx->max_dtable_capacity -
+ nghttp3_min(512, ctx->max_dtable_capacity * 1 / 8);
+ nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
+
+ return ctx->dtable_sum - ent->sum > safe;
+}
+
+int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
+ uint64_t *pmax_cnt, uint64_t *pmin_cnt,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ const nghttp3_nv *nv, uint64_t base,
+ int allow_blocking) {
+ uint32_t hash = 0;
+ int32_t token;
+ nghttp3_qpack_indexing_mode indexing_mode;
+ nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1};
+ nghttp3_qpack_entry *new_ent = NULL;
+ int static_entry;
+ int just_index = 0;
+ int rv;
+
+ token = qpack_lookup_token(nv->name, nv->namelen);
+ static_entry = token != -1 && (size_t)token < nghttp3_arraylen(token_stable);
+ if (static_entry) {
+ hash = token_stable[token].hash;
+ } else {
+ switch (token) {
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ hash = 2952701295u;
+ break;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ hash = 1011170994u;
+ break;
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ hash = 1128642621u;
+ break;
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ hash = 2498028297u;
+ break;
+ }
+ }
+
+ indexing_mode = qpack_encoder_decide_indexing_mode(encoder, nv, token);
+
+ if (static_entry) {
+ sres = nghttp3_qpack_lookup_stable(nv, token, indexing_mode);
+ if (sres.index != -1 && sres.name_value_match) {
+ return nghttp3_qpack_encoder_write_static_indexed(encoder, rbuf,
+ (size_t)sres.index);
+ }
+ }
+
+ if (hash &&
+ nghttp3_map_size(&encoder->streams) < NGHTTP3_QPACK_MAX_QPACK_STREAMS) {
+ dres = nghttp3_qpack_encoder_lookup_dtable(encoder, nv, token, hash,
+ indexing_mode, encoder->krcnt,
+ allow_blocking);
+ just_index = indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_STORE &&
+ dres.pb_index == -1;
+ }
+
+ if (dres.index != -1 && dres.name_value_match) {
+ if (allow_blocking &&
+ qpack_context_check_draining(&encoder->ctx, (size_t)dres.index) &&
+ qpack_encoder_can_index_duplicate(encoder, (size_t)dres.index,
+ *pmin_cnt)) {